Compare commits
No commits in common. "master" and "before-git" have entirely different histories.
master
...
before-git
488 changed files with 358 additions and 32908 deletions
|
@ -1,19 +0,0 @@
|
|||
name: Discord
|
||||
|
||||
on:
|
||||
- workflow_dispatch
|
||||
- push
|
||||
|
||||
jobs:
|
||||
discord_commits:
|
||||
runs-on: ubuntu-latest
|
||||
name: discord commits
|
||||
if: contains(github.event.head_commit.message, '(servers)')
|
||||
|
||||
steps:
|
||||
- name: Discommit
|
||||
uses: https://github.com/matt1432/discommit@v0.0.2
|
||||
with:
|
||||
discord_webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
api_url: 'https://git.nelim.org/api/v1/repos/$OWNER/$REPO/git/commits/$REF'
|
||||
title: 'New commit containing changes to server configs:'
|
5
.gitattributes
vendored
5
.gitattributes
vendored
|
@ -1,5 +0,0 @@
|
|||
flake.lock -diff
|
||||
flake.nix -diff
|
||||
**/non-declarative-conf -diff
|
||||
**/package-lock.json -diff
|
||||
**/HomeAssistantGenerated -diff
|
23
.gitignore
vendored
23
.gitignore
vendored
|
@ -1,23 +0,0 @@
|
|||
# Python
|
||||
*.egg-info
|
||||
|
||||
# NPM
|
||||
*node_modules
|
||||
*build/
|
||||
|
||||
# Direnv
|
||||
*.direnv/
|
||||
|
||||
# Generated by nix
|
||||
result*
|
||||
!results/
|
||||
.nixd.json
|
||||
|
||||
## AGS
|
||||
**/vars.ts
|
||||
**/config.js
|
||||
*icons
|
||||
**/types
|
||||
|
||||
# Other
|
||||
*.temp
|
16
LICENSE.md
16
LICENSE.md
|
@ -1,16 +0,0 @@
|
|||
MIT No Attribution
|
||||
|
||||
Copyright 2024 Mathis H.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
340
README.md
340
README.md
|
@ -1,120 +1,236 @@
|
|||
# My NixOS configs
|
||||
|
||||
TODO: add directory structure info and enforce it
|
||||
- every root folder in the repo represents a flake output except inputs
|
||||
- every root folder only has a `default.nix` and subfolders for each
|
||||
of its attrs
|
||||
- in a subfolder, there should always be a `default.nix`
|
||||
- if there is non nix code, it will be in a `config` folder
|
||||
- redo docs
|
||||
- every module should not do anything if imported
|
||||
|
||||
## AGS
|
||||
|
||||
You might find it weird that most of my config is written in TypeScript.
|
||||
That's because all my desktops run
|
||||
[AGS](https://github.com/Aylur/ags)
|
||||
for UI. Click on
|
||||
[this](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/modules/ags)
|
||||
to see my configuration.
|
||||
|
||||
I'm also a victim of Stockholm syndrome at this point and make my scripts
|
||||
in TypeScript because it's the scripting language I am most comfortable with.
|
||||
|
||||
## About
|
||||
|
||||
### General
|
||||
|
||||
This repo is the complete configuration of machines I own,
|
||||
running NixOS or Nix. Its structure is based on a flake's
|
||||
[outputs](https://wiki.nixos.org/wiki/Flakes#Output_schema).
|
||||
|
||||
### Flake Location
|
||||
|
||||
This git repo will always be located at `$FLAKE` (`config.environment.variables.FLAKE`)
|
||||
and symlinked to `/etc/nixos` to have everything where NixOS tools
|
||||
expect things to be.
|
||||
|
||||
ie.
|
||||
|
||||
```bash
|
||||
sudo rm -r /etc/nixos
|
||||
|
||||
echo "$FLAKE" # /home/matt/.nix
|
||||
|
||||
sudo ln -sf /home/matt/.nix /etc/nixos
|
||||
# Archinstaller
|
||||
```
|
||||
loadkeys ca
|
||||
setfont ter-132b
|
||||
```
|
||||
|
||||
### Flake Outputs
|
||||
|
||||
| Output | Description |
|
||||
| ---------------------------------- | ----------- |
|
||||
| `nixosConfigurations` | [devices](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/devices)' + ISO's configurations |
|
||||
| `nixOnDroidConfigurations.default` | [Nix-On-Droid](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/devices/android)'s configuration |
|
||||
| `packages` | Some custom [packages](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/packages) not available in nixpkgs or modified from it |
|
||||
| `scopedPackages` | Some custom [package scopes](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/scopedPackages) not available in nixpkgs or modified from it |
|
||||
| `apps` | Scripts ran from the flake defined [here](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/apps) |
|
||||
| `homeManagerModules` | [Modules](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/homeManagerModules) made for home-manager |
|
||||
| `homeManagerModules` | [Modules](https://git.nelim.org/matt1432/nixos-configs/src/branch/master/modules) made for NixOS systems |
|
||||
| `formatter` | I format nix code with [alejandra](https://github.com/kamadorueda/alejandra) |
|
||||
| `devShells.default` | A dev shell to build an ISO from the live-image nixosConfiguration |
|
||||
| `devShells.ags` | A dev shell to have a NodeJS env when I enter my AGS's config directory |
|
||||
|
||||
### Flake Inputs
|
||||
|
||||
To allow use of the full nix language for my inputs, I use [genflake](https://github.com/jorsn/flakegen).
|
||||
Therefore, the flake I edit is located at `./outputs.nix`.
|
||||
|
||||
I also prefer using a more descriptive format for my inputs like so:
|
||||
|
||||
```nix
|
||||
nixpkgs = {
|
||||
type = "github";
|
||||
owner = "NixOS";
|
||||
repo = "nixpkgs";
|
||||
|
||||
# Branch name
|
||||
ref = "nixos-unstable";
|
||||
|
||||
# Pin this input to a specific commit
|
||||
rev = "842d9d80cfd4560648c785f8a4e6f3b096790e19";
|
||||
};
|
||||
## Partionning with [cryptsetup](https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition)
|
||||
### Encrypting root partition
|
||||
```
|
||||
$ PART="encrypted partition number ie. 2"
|
||||
$ cryptsetup -y -v luksFormat --type luks1 /dev/nvme0n1p$PART
|
||||
$ cryptsetup open /dev/nvme0n1p$PART root
|
||||
$ mkfs.btrfs /dev/mapper/root
|
||||
$ mount /dev/mapper/root /mnt
|
||||
```
|
||||
### Mounting boot :
|
||||
```
|
||||
$ mount --mkdir /dev/nvme0n1p1 /mnt/boot
|
||||
```
|
||||
|
||||
to make it more clear what is what in the flake URI
|
||||
|
||||
### Secrets
|
||||
|
||||
All my secrets are in a private git repo that makes use of
|
||||
[sops-nix](https://github.com/Mic92/sops-nix).
|
||||
I generate `.sops.yaml` from `.sops.nix`:
|
||||
|
||||
```nix
|
||||
let
|
||||
wim = "somekey";
|
||||
binto = "somekey2";
|
||||
in {
|
||||
creation_rules = [
|
||||
{
|
||||
path_regex = "secrets/[^/]+\\.(yaml|json|env|ini)$";
|
||||
key_groups = [
|
||||
{
|
||||
age = [wim binto];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
### Installing packages on the device
|
||||
```
|
||||
$ pacstrap -K /mnt base linux-firmware linux amd-ucode patch dkms kmod btrfs-progs grub os-prober ntfs-3g efibootmgr efivar networkmanager iwd nano sudo texinfo man-db man-pages
|
||||
```
|
||||
|
||||
which is then converted to `.sops.yaml` using
|
||||
[remarshal](https://github.com/remarshal-project/remarshal)
|
||||
and this shell command:
|
||||
|
||||
```bash
|
||||
nix eval --json --file ./.sops.nix | remarshal --if json --of yaml > .sops.yaml
|
||||
## Preparing for chroot
|
||||
```
|
||||
$ genfstab -U /mnt >> /mnt/etc/fstab
|
||||
$ arch-chroot /mnt
|
||||
```
|
||||
|
||||
TLDR: I
|
||||
**[hate](https://ruudvanasseldonk.com/2023/01/11/the-yaml-document-from-hell)**
|
||||
YAML
|
||||
# Chroot in Installed Arch
|
||||
```
|
||||
$ ln -sf /usr/share/zoneinfo/America/Montreal /etc/localtime
|
||||
$ hwclock --systohc
|
||||
$ echo matt-laptop > /etc/hostname
|
||||
$ passwd
|
||||
```
|
||||
|
||||
## Localization
|
||||
Uncomment ca_FR.UTF-8 en_CA.UTF-8 en_US.UTF-8 fr_CA.UTF-8 and run
|
||||
```
|
||||
$ locale-gen
|
||||
$ echo LANG=en_CA.UTF-8 > /etc/locale.conf
|
||||
$ echo KEYMAP=ca > /etc/vconsole.conf
|
||||
```
|
||||
## Edit /etc/mkinitcpio.conf for LUKS
|
||||
```
|
||||
$ sed -i 's/BINARIES=.*/BINARIES=(btrfs)/' /etc/mkinitcpio.conf
|
||||
...
|
||||
$ sed -i 's/HOOKS=.*/HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt filesystems fsck)/' /etc/mkinitcpio.conf
|
||||
```
|
||||
then run ```mkinitpcio -P```
|
||||
|
||||
## Grub install
|
||||
```
|
||||
$ grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=wim --boot-directory=/boot/EFI/wim
|
||||
```
|
||||
|
||||
### Edit /etc/default/grub for LUKS
|
||||
```
|
||||
$ CRYPT="cryptdevice=$(blkid | sed -n 's/.*nvme0n1p'$PART': \(.*\) TYPE.*/\1/p'):root"
|
||||
$ sed -i 's#GRUB_CMDLINE_LINUX_DEFAULT.*#GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel 3 '$CRYPT' root=/dev/mapper/root"#' /etc/default/grub
|
||||
```
|
||||
make sure the UUID is the actual partition inside the LUKS container and run ```grub-mkconfig -o /boot/EFI/wim/grub/grub.cfg```
|
||||
|
||||
we can now reboot to the installed Arch
|
||||
<br/><br/>
|
||||
|
||||
# Inside installed Arch
|
||||
|
||||
## Configure [internet](https://wiki.archlinux.org/title/Iwd) access
|
||||
```
|
||||
$ systemctl enable --now NetworkManager systemd-networkd systemd-resolved systemd-timesyncd
|
||||
|
||||
$ cat << EOF >> /etc/NetworkManager/conf.d/wifi_backend.conf
|
||||
[device]
|
||||
wifi.backend=iwd
|
||||
EOF
|
||||
|
||||
$ cat << EOF >> /etc/iwd/main.conf
|
||||
[General]
|
||||
EnableNetworkConfiguration=true
|
||||
EOF
|
||||
|
||||
$ systemctl stop iwd
|
||||
$ systemctl restart NetworkManager
|
||||
$ iwctl device list # check if powered on
|
||||
$ iwctl station wlan0 scan
|
||||
$ iwctl station wlan0 get-networks
|
||||
$ iwctl station wlan0 connect $SSID
|
||||
```
|
||||
|
||||
### Configure systemd-resolved
|
||||
```
|
||||
$ ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||
$ sed -i 's/#DNS=.*/DNS=100.64.0.1/' /etc/systemd/resolved.conf
|
||||
$ sed -i 's/#FallbackDNS=.*/FallbackDNS=1.1.1.1/' /etc/systemd/resolved.conf
|
||||
$ sed -i 's/#DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
|
||||
```
|
||||
|
||||
### Configure reflector for mirror management of pacman
|
||||
```
|
||||
$ pacman -Sy reflector
|
||||
$ nano /etc/xdg/reflector/reflector.conf
|
||||
$ systemctl enable --now reflector.timer
|
||||
```
|
||||
|
||||
## User management
|
||||
```
|
||||
$ useradd -m matt -G wheel,input
|
||||
$ passwd matt
|
||||
$ env EDITOR=nano visudo # set wheel to allow sudo commands with pass
|
||||
```
|
||||
|
||||
## A lot of packages to install
|
||||
```
|
||||
$ pacman -Sy htop pkgfile mlocate rsync tmux mosh usbutils wget git curl devtools mesa lib32-mesa vulkan-radeon lib32-vulkan-radeon libva-mesa-driver lib32-libva-mesa-driver mesa-vdpau lib32-mesa-vdpau bash-completion fzf
|
||||
$ pkgfile --update
|
||||
```
|
||||
|
||||
## su matt
|
||||
|
||||
## Install paru
|
||||
```
|
||||
$ sudo pacman -S --needed git base-devel
|
||||
$ git clone https://aur.archlinux.org/paru-git.git
|
||||
$ cd paru-git
|
||||
$ makepkg -si
|
||||
|
||||
$ sed -i 's/#Color/Color/' /etc/pacman.conf
|
||||
$ sed -i 's/#IgnorePkg.*/IgnorePkg = linux-xanmod-anbox linux-xanmod-anbox-headers/' /etc/pacman.conf
|
||||
|
||||
$ cat << EOF >> /etc/paru.conf
|
||||
CombinedUpgrade
|
||||
BatchInstall
|
||||
BottomUp
|
||||
NoWarn = plymouth-theme-arch-elegant
|
||||
EOF
|
||||
```
|
||||
|
||||
## Audio
|
||||
### ALSA
|
||||
```
|
||||
$ yay -Sy alsa-utils alsa-firmware sof-firmware alsa-ucm-conf
|
||||
|
||||
#unmute speakers
|
||||
$ amixer sset Master unmute
|
||||
```
|
||||
|
||||
### Pipewire
|
||||
```
|
||||
$ yay -Sy pipewire-audio pipewire-alsa pipewire-pulse
|
||||
$ yay -R pulseaudio-alsa
|
||||
$ sudo systemctl stop pulseaudio.service
|
||||
$ systemctl --user enable --now pipewire-pulse.service
|
||||
```
|
||||
|
||||
## Fingerprint Sensor Hack
|
||||
```
|
||||
$ yay -Sy python pam-fprint-grosshack
|
||||
$ sudo systemctl enable --now fprintd
|
||||
$ fprintd-enroll
|
||||
```
|
||||
|
||||
### Use the reader
|
||||
add this to the top of every file in /etc/pam.d/ that you want ie. polkit-1, sudo uwu
|
||||
```
|
||||
auth sufficient pam_fprintd_grosshack.so
|
||||
auth sufficient pam_unix.so try_first_pass nullok
|
||||
```
|
||||
OR (for gtklock and check for sddm)
|
||||
```
|
||||
auth sufficient pam_fprintd.so
|
||||
```
|
||||
|
||||
## Plymouth and Silent Boot
|
||||
By following the wiki pages on [watchdogs](https://wiki.archlinux.org/title/Improving_performance#Watchdogs), [silent booting](https://wiki.archlinux.org/title/Silent_boot#top-page) and [Plymouth](https://wiki.archlinux.org/title/Plymouth), I edited my grub config and mkinitcpio, installed and setup Plymouth, to get a satisfying booting experience
|
||||
```
|
||||
$ yay -Sy plymouth-git
|
||||
```
|
||||
/etc/mkinitcpio.conf
|
||||
```
|
||||
$ sudo sed -i 's/MODULES=()/MODULES=(amdgpu)/' /etc/mkinitcpio.conf
|
||||
$ sudo sed -i 's/#COMPRESSION="lz4"/COMPRESSION="lz4"/' /etc/mkinitcpio.conf
|
||||
$ sudo sed -i 's/HOOKS=(.* /HOOKS=(base udev plymouth encrypt autodetect modconf kms keyboard keymap consolefont block filesystems fsck)/' /etc/mkinitcpio.conf
|
||||
COMPRESSION="lz4"
|
||||
```
|
||||
/etc/default/grub
|
||||
```
|
||||
sudo sed -i 's/quiet loglevel 3/quiet splash loglevel=3 systemd.show_status=auto rd.udev.log_level=3 splash nowatchdog psi=1/' /etc/default/grub
|
||||
```
|
||||
Mute watchdog
|
||||
```
|
||||
$ echo blacklist sp5100_tco | sudo tee /etc/modprobe.d/disable-sp5100-watchdog.conf
|
||||
```
|
||||
Apply changes [Theme](https://github.com/murkl/plymouth-theme-arch-elegant)
|
||||
```
|
||||
$ git clone https://github.com/murkl/plymouth-theme-arch-elegant.git
|
||||
$ cd plymouth-theme-arch-elegant/aur
|
||||
$ makepkg -si
|
||||
$ sudo plymouth-set-default-theme -R arch-elegant
|
||||
$ sudo grub-mkconfig -o /boot/grub/grub.cfg
|
||||
$ sudo sed -i 's/echo/#ech~o/g' /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
## Here are some random changes and tweaks
|
||||
|
||||
### Firefox touchscreen [tweak](https://wiki.archlinux.org/title/Firefox/Tweaks#Enable_touchscreen_gestures)
|
||||
```
|
||||
$ echo MOZ_USE_XINPUT2 DEFAULT=1 | sudo tee -a /etc/security/pam_env.conf
|
||||
```
|
||||
then logout
|
||||
|
||||
### More Packages that are most likely needed
|
||||
```
|
||||
run toinstall.sh script
|
||||
$ sudo reboot
|
||||
```
|
||||
|
||||
### Flatpak
|
||||
```
|
||||
$ flatpak install com.github.iwalton3.jellyfin-media-player com.github.tchx84.Flatseal xournalpp stemlink
|
||||
$ sudo flatpak override --filesystem=xdg-config/gtk-3.0
|
||||
```
|
||||
|
||||
## Finally, install dotfiles
|
||||
### get access to repo first
|
||||
```
|
||||
$ mkdir ~/git && cd ~/git
|
||||
$ git clone git@git.nelim.org:matt1432/dotfiles.git
|
||||
$ cd dotfiles
|
||||
$ bash getenv.sh
|
||||
$ sudo bash setup.sh
|
||||
$ sudo bash fzf.sh /usr/share/fzf
|
||||
```
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
use flake $FLAKE#node
|
||||
npm ci
|
|
@ -1,451 +0,0 @@
|
|||
import eslint from '@eslint/js';
|
||||
import jsdoc from 'eslint-plugin-jsdoc';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
|
||||
export default tseslint.config({
|
||||
files: ['**/*.js', '**/*.ts'],
|
||||
ignores: ['node_modules/**', 'types/**'],
|
||||
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
jsdoc.configs['flat/recommended-typescript'],
|
||||
stylistic.configs['recommended-flat'],
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
],
|
||||
|
||||
rules: {
|
||||
// JSDoc settings
|
||||
'jsdoc/tag-lines': ['warn', 'any', { startLines: 1 }],
|
||||
'jsdoc/check-line-alignment': ['warn', 'always', {
|
||||
tags: ['param', 'arg', 'argument', 'property', 'prop'],
|
||||
}],
|
||||
'jsdoc/no-types': 'off',
|
||||
|
||||
// Newer settings
|
||||
'@typescript-eslint/no-extraneous-class': ['off'],
|
||||
'@typescript-eslint/no-implied-eval': ['off'],
|
||||
'class-methods-use-this': 'off',
|
||||
'@stylistic/no-multiple-empty-lines': 'off',
|
||||
|
||||
// Pre-flat config
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'all',
|
||||
argsIgnorePattern: '^_',
|
||||
caughtErrors: 'all',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
|
||||
'array-callback-return': [
|
||||
'error',
|
||||
{
|
||||
allowImplicit: true,
|
||||
checkForEach: true,
|
||||
},
|
||||
],
|
||||
'no-constructor-return': [
|
||||
'error',
|
||||
],
|
||||
'no-unreachable-loop': [
|
||||
'error',
|
||||
{
|
||||
ignore: [
|
||||
'ForInStatement',
|
||||
'ForOfStatement',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-use-before-define': [
|
||||
'error',
|
||||
{
|
||||
functions: false,
|
||||
},
|
||||
],
|
||||
'block-scoped-var': [
|
||||
'error',
|
||||
],
|
||||
'curly': [
|
||||
'warn',
|
||||
],
|
||||
'default-case-last': [
|
||||
'warn',
|
||||
],
|
||||
'default-param-last': [
|
||||
'error',
|
||||
],
|
||||
'eqeqeq': [
|
||||
'error',
|
||||
'smart',
|
||||
],
|
||||
'func-names': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'func-style': [
|
||||
'warn',
|
||||
'expression',
|
||||
],
|
||||
'logical-assignment-operators': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'no-array-constructor': [
|
||||
'error',
|
||||
],
|
||||
'no-empty-function': [
|
||||
'warn',
|
||||
],
|
||||
'no-empty-static-block': [
|
||||
'warn',
|
||||
],
|
||||
'no-extend-native': [
|
||||
'error',
|
||||
],
|
||||
'no-extra-bind': [
|
||||
'warn',
|
||||
],
|
||||
'no-implicit-coercion': [
|
||||
'warn',
|
||||
],
|
||||
'no-iterator': [
|
||||
'error',
|
||||
],
|
||||
'no-labels': [
|
||||
'error',
|
||||
],
|
||||
'no-lone-blocks': [
|
||||
'error',
|
||||
],
|
||||
'no-lonely-if': [
|
||||
'error',
|
||||
],
|
||||
'no-loop-func': [
|
||||
'error',
|
||||
],
|
||||
'no-magic-numbers': [
|
||||
'error',
|
||||
{
|
||||
ignore: [
|
||||
-1,
|
||||
0.1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
10,
|
||||
12,
|
||||
33,
|
||||
66,
|
||||
100,
|
||||
255,
|
||||
360,
|
||||
450,
|
||||
500,
|
||||
1000,
|
||||
],
|
||||
ignoreDefaultValues: true,
|
||||
ignoreClassFieldInitialValues: true,
|
||||
},
|
||||
],
|
||||
'no-multi-assign': [
|
||||
'error',
|
||||
],
|
||||
'no-new-wrappers': [
|
||||
'error',
|
||||
],
|
||||
'no-object-constructor': [
|
||||
'error',
|
||||
],
|
||||
'no-proto': [
|
||||
'error',
|
||||
],
|
||||
'no-return-assign': [
|
||||
'error',
|
||||
],
|
||||
'no-sequences': [
|
||||
'error',
|
||||
],
|
||||
'no-shadow': [
|
||||
'error',
|
||||
{
|
||||
builtinGlobals: true,
|
||||
allow: [
|
||||
'Window',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-undef-init': [
|
||||
'warn',
|
||||
],
|
||||
'no-undefined': [
|
||||
'error',
|
||||
],
|
||||
'no-useless-constructor': [
|
||||
'warn',
|
||||
],
|
||||
'no-useless-escape': [
|
||||
'off',
|
||||
],
|
||||
'no-useless-return': [
|
||||
'error',
|
||||
],
|
||||
'no-var': [
|
||||
'error',
|
||||
],
|
||||
'no-void': [
|
||||
'off',
|
||||
],
|
||||
'no-with': [
|
||||
'error',
|
||||
],
|
||||
'object-shorthand': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'one-var': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'operator-assignment': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'prefer-arrow-callback': [
|
||||
'error',
|
||||
],
|
||||
'prefer-const': [
|
||||
'error',
|
||||
],
|
||||
'prefer-object-has-own': [
|
||||
'error',
|
||||
],
|
||||
'prefer-regex-literals': [
|
||||
'error',
|
||||
],
|
||||
'prefer-template': [
|
||||
'warn',
|
||||
],
|
||||
'no-prototype-builtins': 'off',
|
||||
'@typescript-eslint/no-var-requires': [
|
||||
'off',
|
||||
],
|
||||
'@stylistic/array-bracket-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/array-bracket-spacing': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/arrow-parens': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/brace-style': [
|
||||
'warn',
|
||||
'stroustrup',
|
||||
],
|
||||
'@stylistic/comma-dangle': [
|
||||
'warn',
|
||||
'always-multiline',
|
||||
],
|
||||
'@stylistic/comma-spacing': [
|
||||
'warn',
|
||||
{
|
||||
before: false,
|
||||
after: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/comma-style': [
|
||||
'error',
|
||||
'last',
|
||||
],
|
||||
'@stylistic/dot-location': [
|
||||
'error',
|
||||
'property',
|
||||
],
|
||||
'@stylistic/function-call-argument-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/function-paren-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'@stylistic/indent': [
|
||||
'warn',
|
||||
4,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
ignoreComments: true,
|
||||
ignoredNodes: ['TemplateLiteral > *'],
|
||||
},
|
||||
],
|
||||
'@stylistic/key-spacing': [
|
||||
'warn',
|
||||
{
|
||||
beforeColon: false,
|
||||
afterColon: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/keyword-spacing': [
|
||||
'warn',
|
||||
{
|
||||
before: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/linebreak-style': [
|
||||
'error',
|
||||
'unix',
|
||||
],
|
||||
'@stylistic/lines-between-class-members': [
|
||||
'warn',
|
||||
'always',
|
||||
{
|
||||
exceptAfterSingleLine: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/max-len': [
|
||||
'warn',
|
||||
{
|
||||
code: 105,
|
||||
ignoreComments: true,
|
||||
ignoreTrailingComments: true,
|
||||
ignoreUrls: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/multiline-ternary': [
|
||||
'warn',
|
||||
'always-multiline',
|
||||
],
|
||||
'@stylistic/new-parens': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-mixed-operators': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/no-mixed-spaces-and-tabs': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-multi-spaces': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-tabs': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-trailing-spaces': [
|
||||
'error',
|
||||
],
|
||||
'@stylistic/no-whitespace-before-property': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/nonblock-statement-body-position': [
|
||||
'error',
|
||||
'below',
|
||||
],
|
||||
'@stylistic/object-curly-newline': [
|
||||
'warn',
|
||||
{
|
||||
consistent: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/object-curly-spacing': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/operator-linebreak': [
|
||||
'warn',
|
||||
'after',
|
||||
],
|
||||
'@stylistic/padded-blocks': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/padding-line-between-statements': [
|
||||
'warn',
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: '*',
|
||||
next: 'return',
|
||||
},
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
next: '*',
|
||||
},
|
||||
{
|
||||
blankLine: 'any',
|
||||
prev: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
next: [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
],
|
||||
},
|
||||
{
|
||||
blankLine: 'always',
|
||||
prev: [
|
||||
'case',
|
||||
'default',
|
||||
],
|
||||
next: '*',
|
||||
},
|
||||
],
|
||||
'@stylistic/quote-props': [
|
||||
'error',
|
||||
'consistent-as-needed',
|
||||
],
|
||||
'@stylistic/quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{
|
||||
avoidEscape: true,
|
||||
},
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/semi-spacing': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/space-before-blocks': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/space-before-function-paren': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'@stylistic/space-infix-ops': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/spaced-comment': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'@stylistic/switch-colon-spacing': [
|
||||
'warn',
|
||||
],
|
||||
'@stylistic/wrap-regex': [
|
||||
'warn',
|
||||
],
|
||||
},
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
import eslintConf from './eslint.config';
|
||||
|
||||
export default eslintConf;
|
BIN
apps/config/package-lock.json
generated
BIN
apps/config/package-lock.json
generated
Binary file not shown.
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "eslint-conf",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": "./index.ts",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.17.0",
|
||||
"@stylistic/eslint-plugin": "2.12.1",
|
||||
"eslint": "9.17.0",
|
||||
"eslint-plugin-jsdoc": "50.6.1",
|
||||
"jiti": "2.4.2",
|
||||
"pkg-types": "1.2.1",
|
||||
"typescript": "5.7.2",
|
||||
"typescript-eslint": "8.18.1"
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
// Env
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
// Module
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "bundler",
|
||||
"baseUrl": ".",
|
||||
// Emit
|
||||
"noEmit": true,
|
||||
"newLine": "LF",
|
||||
// Interop
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
// Type Checking
|
||||
"strict": true,
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.base.json",
|
||||
"includes": [
|
||||
"*.ts",
|
||||
"**/*.ts",
|
||||
"*.js",
|
||||
"**/*.js"
|
||||
]
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
inputs,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (pkgs.lib) getExe listToAttrs nameValuePair;
|
||||
|
||||
buildApp = attrs: (pkgs.callPackage ./nix/buildApp.nix ({} // inputs // attrs));
|
||||
|
||||
mkApp = file: {
|
||||
program = getExe (pkgs.callPackage file ({inherit buildApp;} // inputs));
|
||||
type = "app";
|
||||
};
|
||||
|
||||
mkApps = apps: listToAttrs (map (x: nameValuePair x (mkApp ./${x})) apps);
|
||||
in
|
||||
mkApps [
|
||||
"extract-subs"
|
||||
"update-sources"
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
use flake $FLAKE#subtitles-dev
|
||||
npm ci
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
buildApp,
|
||||
ffmpeg-full,
|
||||
...
|
||||
}:
|
||||
buildApp {
|
||||
src = ./.;
|
||||
npmDepsHash = "sha256-ytaO1mqoORNB5rP/8X/WpIoHkxmi1+Pc6Nep02+fTZ8=";
|
||||
|
||||
runtimeInputs = [
|
||||
ffmpeg-full
|
||||
];
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import eslintConf from 'eslint-conf';
|
||||
|
||||
export default eslintConf;
|
BIN
apps/extract-subs/package-lock.json
generated
BIN
apps/extract-subs/package-lock.json
generated
Binary file not shown.
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "extract-subs",
|
||||
"version": "0.0.0",
|
||||
"bin": "out/bin/app.cjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/fluent-ffmpeg": "2.1.27",
|
||||
"fluent-ffmpeg": "2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-conf": "file:../config",
|
||||
"@types/node": "22.10.2",
|
||||
"esbuild": "0.24.2",
|
||||
"eslint": "9.17.0",
|
||||
"jiti": "2.4.2",
|
||||
"typescript": "5.7.2"
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
import { spawnSync as spawn } from 'child_process';
|
||||
|
||||
import ffprobe from './ffprobe';
|
||||
import { ISO6393To1 } from './lang-codes';
|
||||
|
||||
/* Types */
|
||||
import { FfprobeStream } from 'fluent-ffmpeg';
|
||||
|
||||
|
||||
const SPAWN_OPTS = {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* These are the cli arguments
|
||||
*
|
||||
* @param videoPath the directory in which we want to sync the subtitles
|
||||
* @param languages a comma-separated list of languages (3 letters) to sync the subtitles
|
||||
*/
|
||||
const video = process.argv[2];
|
||||
const languages = process.argv[3]?.split(',');
|
||||
|
||||
|
||||
// Global Vars
|
||||
const subIndexes: number[] = [];
|
||||
let videoPath: string;
|
||||
let baseName: string;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the relative path to the subtitle file of a ffmpeg stream.
|
||||
*
|
||||
* @param sub the stream of the subtitles to extract
|
||||
* @returns the path of the subtitle file
|
||||
*/
|
||||
const getSubPath = (sub: FfprobeStream): string => {
|
||||
const language = ISO6393To1.get(sub.tags.language);
|
||||
|
||||
const forced = sub.disposition?.forced === 0 ?
|
||||
'' :
|
||||
'.forced';
|
||||
|
||||
const hearingImpaired = sub.disposition?.hearing_impaired === 0 ?
|
||||
'' :
|
||||
'.sdh';
|
||||
|
||||
return `${baseName}${forced}.${language}${hearingImpaired}.srt`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all subtitles streams from the video file.
|
||||
*/
|
||||
const removeContainerSubs = (): void => {
|
||||
spawn('mv', [
|
||||
videoPath,
|
||||
`${videoPath}.bak`,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
spawn('ffmpeg', [
|
||||
'-i', `${videoPath}.bak`,
|
||||
'-map', '0',
|
||||
...subIndexes.map((i) => ['-map', `-0:${i}`]).flat(),
|
||||
'-c', 'copy', videoPath,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
spawn('rm', [
|
||||
`${videoPath}.bak`,
|
||||
], SPAWN_OPTS);
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts a sub of a video file to a subtitle file.
|
||||
*
|
||||
* @param sub the stream of the subtitles to extract
|
||||
*/
|
||||
const extractSub = (sub: FfprobeStream): void => {
|
||||
const subFile = getSubPath(sub);
|
||||
|
||||
spawn('ffmpeg', [
|
||||
'-i', videoPath,
|
||||
'-map', `0:${sub.index}`, subFile,
|
||||
], SPAWN_OPTS);
|
||||
|
||||
subIndexes.push(sub.index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts the list of streams to only keep subtitles
|
||||
* that can be extracted.
|
||||
*
|
||||
* @param lang the language of the subtitles
|
||||
* @param streams the streams
|
||||
* @returns the streams that represent subtitles
|
||||
*/
|
||||
const findSubs = (
|
||||
lang: string,
|
||||
streams: FfprobeStream[],
|
||||
): FfprobeStream[] => {
|
||||
const subs = streams.filter((s) => s.tags?.language &&
|
||||
s.tags.language === lang &&
|
||||
s.codec_type === 'subtitle');
|
||||
|
||||
const pgs = subs.filter((s) => s.codec_name === 'hdmv_pgs_subtitle');
|
||||
|
||||
// If we only have PGS subs, warn user
|
||||
if (pgs.length === subs.length) {
|
||||
console.warn(`No SRT subtitle tracks were found for ${lang}`);
|
||||
}
|
||||
|
||||
// Remove PGS streams from subs
|
||||
return subs.filter((s) => s.codec_name !== 'hdmv_pgs_subtitle');
|
||||
};
|
||||
|
||||
/**
|
||||
* Where the magic happens.
|
||||
*/
|
||||
const main = async(): Promise<void> => {
|
||||
// Get rid of video extension
|
||||
baseName = videoPath.split('/').at(-1)!.replace(/\.[^.]*$/, '');
|
||||
|
||||
// ffprobe the video file to see available sub tracks
|
||||
const data = await ffprobe(videoPath);
|
||||
|
||||
if (!data?.streams) {
|
||||
console.error('Couldn\'t find streams in video file');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for languages wanted
|
||||
languages.forEach((lang) => {
|
||||
const subs = findSubs(lang, data.streams);
|
||||
|
||||
if (subs.length === 0) {
|
||||
console.warn(`No subtitle tracks were found for ${lang}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract all subs
|
||||
subs.forEach((sub) => { extractSub(sub); });
|
||||
});
|
||||
|
||||
removeContainerSubs();
|
||||
};
|
||||
|
||||
|
||||
// Check if there are 2 params
|
||||
if (video && languages) {
|
||||
videoPath = video;
|
||||
main();
|
||||
}
|
||||
else {
|
||||
console.error('Error: no argument passed');
|
||||
process.exit(1);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import Ffmpeg from 'fluent-ffmpeg';
|
||||
|
||||
|
||||
export default (videoPath: string) => new Promise<Ffmpeg.FfprobeData>((resolve) => {
|
||||
Ffmpeg.ffprobe(videoPath, (_e, data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
|
@ -1,373 +0,0 @@
|
|||
export const ISO6391To3 = new Map([
|
||||
['aa', 'aar'],
|
||||
['ab', 'abk'],
|
||||
['af', 'afr'],
|
||||
['ak', 'aka'],
|
||||
['am', 'amh'],
|
||||
['ar', 'ara'],
|
||||
['an', 'arg'],
|
||||
['as', 'asm'],
|
||||
['av', 'ava'],
|
||||
['ae', 'ave'],
|
||||
['ay', 'aym'],
|
||||
['az', 'aze'],
|
||||
['ba', 'bak'],
|
||||
['bm', 'bam'],
|
||||
['be', 'bel'],
|
||||
['bn', 'ben'],
|
||||
['bi', 'bis'],
|
||||
['bo', 'bod'],
|
||||
['bs', 'bos'],
|
||||
['br', 'bre'],
|
||||
['bg', 'bul'],
|
||||
['ca', 'cat'],
|
||||
['cs', 'ces'],
|
||||
['ch', 'cha'],
|
||||
['ce', 'che'],
|
||||
['cu', 'chu'],
|
||||
['cv', 'chv'],
|
||||
['kw', 'cor'],
|
||||
['co', 'cos'],
|
||||
['cr', 'cre'],
|
||||
['cy', 'cym'],
|
||||
['da', 'dan'],
|
||||
['de', 'deu'],
|
||||
['dv', 'div'],
|
||||
['dz', 'dzo'],
|
||||
['el', 'ell'],
|
||||
['en', 'eng'],
|
||||
['eo', 'epo'],
|
||||
['et', 'est'],
|
||||
['eu', 'eus'],
|
||||
['ee', 'ewe'],
|
||||
['fo', 'fao'],
|
||||
['fa', 'fas'],
|
||||
['fj', 'fij'],
|
||||
['fi', 'fin'],
|
||||
['fr', 'fre'],
|
||||
['fy', 'fry'],
|
||||
['ff', 'ful'],
|
||||
['gd', 'gla'],
|
||||
['ga', 'gle'],
|
||||
['gl', 'glg'],
|
||||
['gv', 'glv'],
|
||||
['gn', 'grn'],
|
||||
['gu', 'guj'],
|
||||
['ht', 'hat'],
|
||||
['ha', 'hau'],
|
||||
['sh', 'hbs'],
|
||||
['he', 'heb'],
|
||||
['hz', 'her'],
|
||||
['hi', 'hin'],
|
||||
['ho', 'hmo'],
|
||||
['hr', 'hrv'],
|
||||
['hu', 'hun'],
|
||||
['hy', 'hye'],
|
||||
['ig', 'ibo'],
|
||||
['io', 'ido'],
|
||||
['ii', 'iii'],
|
||||
['iu', 'iku'],
|
||||
['ie', 'ile'],
|
||||
['ia', 'ina'],
|
||||
['id', 'ind'],
|
||||
['ik', 'ipk'],
|
||||
['is', 'isl'],
|
||||
['it', 'ita'],
|
||||
['jv', 'jav'],
|
||||
['ja', 'jpn'],
|
||||
['kl', 'kal'],
|
||||
['kn', 'kan'],
|
||||
['ks', 'kas'],
|
||||
['ka', 'kat'],
|
||||
['kr', 'kau'],
|
||||
['kk', 'kaz'],
|
||||
['km', 'khm'],
|
||||
['ki', 'kik'],
|
||||
['rw', 'kin'],
|
||||
['ky', 'kir'],
|
||||
['kv', 'kom'],
|
||||
['kg', 'kon'],
|
||||
['ko', 'kor'],
|
||||
['kj', 'kua'],
|
||||
['ku', 'kur'],
|
||||
['lo', 'lao'],
|
||||
['la', 'lat'],
|
||||
['lv', 'lav'],
|
||||
['li', 'lim'],
|
||||
['ln', 'lin'],
|
||||
['lt', 'lit'],
|
||||
['lb', 'ltz'],
|
||||
['lu', 'lub'],
|
||||
['lg', 'lug'],
|
||||
['mh', 'mah'],
|
||||
['ml', 'mal'],
|
||||
['mr', 'mar'],
|
||||
['mk', 'mkd'],
|
||||
['mg', 'mlg'],
|
||||
['mt', 'mlt'],
|
||||
['mn', 'mon'],
|
||||
['mi', 'mri'],
|
||||
['ms', 'msa'],
|
||||
['my', 'mya'],
|
||||
['na', 'nau'],
|
||||
['nv', 'nav'],
|
||||
['nr', 'nbl'],
|
||||
['nd', 'nde'],
|
||||
['ng', 'ndo'],
|
||||
['ne', 'nep'],
|
||||
['nl', 'nld'],
|
||||
['nn', 'nno'],
|
||||
['nb', 'nob'],
|
||||
['no', 'nor'],
|
||||
['ny', 'nya'],
|
||||
['oc', 'oci'],
|
||||
['oj', 'oji'],
|
||||
['or', 'ori'],
|
||||
['om', 'orm'],
|
||||
['os', 'oss'],
|
||||
['pa', 'pan'],
|
||||
['pi', 'pli'],
|
||||
['pl', 'pol'],
|
||||
['pt', 'por'],
|
||||
['ps', 'pus'],
|
||||
['qu', 'que'],
|
||||
['rm', 'roh'],
|
||||
['ro', 'ron'],
|
||||
['rn', 'run'],
|
||||
['ru', 'rus'],
|
||||
['sg', 'sag'],
|
||||
['sa', 'san'],
|
||||
['si', 'sin'],
|
||||
['sk', 'slk'],
|
||||
['sl', 'slv'],
|
||||
['se', 'sme'],
|
||||
['sm', 'smo'],
|
||||
['sn', 'sna'],
|
||||
['sd', 'snd'],
|
||||
['so', 'som'],
|
||||
['st', 'sot'],
|
||||
['es', 'spa'],
|
||||
['sq', 'sqi'],
|
||||
['sc', 'srd'],
|
||||
['sr', 'srp'],
|
||||
['ss', 'ssw'],
|
||||
['su', 'sun'],
|
||||
['sw', 'swa'],
|
||||
['sv', 'swe'],
|
||||
['ty', 'tah'],
|
||||
['ta', 'tam'],
|
||||
['tt', 'tat'],
|
||||
['te', 'tel'],
|
||||
['tg', 'tgk'],
|
||||
['tl', 'tgl'],
|
||||
['th', 'tha'],
|
||||
['ti', 'tir'],
|
||||
['to', 'ton'],
|
||||
['tn', 'tsn'],
|
||||
['ts', 'tso'],
|
||||
['tk', 'tuk'],
|
||||
['tr', 'tur'],
|
||||
['tw', 'twi'],
|
||||
['ug', 'uig'],
|
||||
['uk', 'ukr'],
|
||||
['ur', 'urd'],
|
||||
['uz', 'uzb'],
|
||||
['ve', 'ven'],
|
||||
['vi', 'vie'],
|
||||
['vo', 'vol'],
|
||||
['wa', 'wln'],
|
||||
['wo', 'wol'],
|
||||
['xh', 'xho'],
|
||||
['yi', 'yid'],
|
||||
['yo', 'yor'],
|
||||
['za', 'zha'],
|
||||
['zh', 'zho'],
|
||||
['zu', 'zul'],
|
||||
]);
|
||||
|
||||
export const ISO6393To1 = new Map([
|
||||
['aar', 'aa'],
|
||||
['abk', 'ab'],
|
||||
['afr', 'af'],
|
||||
['aka', 'ak'],
|
||||
['amh', 'am'],
|
||||
['ara', 'ar'],
|
||||
['arg', 'an'],
|
||||
['asm', 'as'],
|
||||
['ava', 'av'],
|
||||
['ave', 'ae'],
|
||||
['aym', 'ay'],
|
||||
['aze', 'az'],
|
||||
['bak', 'ba'],
|
||||
['bam', 'bm'],
|
||||
['bel', 'be'],
|
||||
['ben', 'bn'],
|
||||
['bis', 'bi'],
|
||||
['bod', 'bo'],
|
||||
['bos', 'bs'],
|
||||
['bre', 'br'],
|
||||
['bul', 'bg'],
|
||||
['cat', 'ca'],
|
||||
['ces', 'cs'],
|
||||
['cha', 'ch'],
|
||||
['che', 'ce'],
|
||||
['chu', 'cu'],
|
||||
['chv', 'cv'],
|
||||
['cor', 'kw'],
|
||||
['cos', 'co'],
|
||||
['cre', 'cr'],
|
||||
['cym', 'cy'],
|
||||
['dan', 'da'],
|
||||
['deu', 'de'],
|
||||
['div', 'dv'],
|
||||
['dzo', 'dz'],
|
||||
['ell', 'el'],
|
||||
['eng', 'en'],
|
||||
['epo', 'eo'],
|
||||
['est', 'et'],
|
||||
['eus', 'eu'],
|
||||
['ewe', 'ee'],
|
||||
['fao', 'fo'],
|
||||
['fas', 'fa'],
|
||||
['fij', 'fj'],
|
||||
['fin', 'fi'],
|
||||
['fre', 'fr'],
|
||||
['fry', 'fy'],
|
||||
['ful', 'ff'],
|
||||
['gla', 'gd'],
|
||||
['gle', 'ga'],
|
||||
['glg', 'gl'],
|
||||
['glv', 'gv'],
|
||||
['grn', 'gn'],
|
||||
['guj', 'gu'],
|
||||
['hat', 'ht'],
|
||||
['hau', 'ha'],
|
||||
['hbs', 'sh'],
|
||||
['heb', 'he'],
|
||||
['her', 'hz'],
|
||||
['hin', 'hi'],
|
||||
['hmo', 'ho'],
|
||||
['hrv', 'hr'],
|
||||
['hun', 'hu'],
|
||||
['hye', 'hy'],
|
||||
['ibo', 'ig'],
|
||||
['ido', 'io'],
|
||||
['iii', 'ii'],
|
||||
['iku', 'iu'],
|
||||
['ile', 'ie'],
|
||||
['ina', 'ia'],
|
||||
['ind', 'id'],
|
||||
['ipk', 'ik'],
|
||||
['isl', 'is'],
|
||||
['ita', 'it'],
|
||||
['jav', 'jv'],
|
||||
['jpn', 'ja'],
|
||||
['kal', 'kl'],
|
||||
['kan', 'kn'],
|
||||
['kas', 'ks'],
|
||||
['kat', 'ka'],
|
||||
['kau', 'kr'],
|
||||
['kaz', 'kk'],
|
||||
['khm', 'km'],
|
||||
['kik', 'ki'],
|
||||
['kin', 'rw'],
|
||||
['kir', 'ky'],
|
||||
['kom', 'kv'],
|
||||
['kon', 'kg'],
|
||||
['kor', 'ko'],
|
||||
['kua', 'kj'],
|
||||
['kur', 'ku'],
|
||||
['lao', 'lo'],
|
||||
['lat', 'la'],
|
||||
['lav', 'lv'],
|
||||
['lim', 'li'],
|
||||
['lin', 'ln'],
|
||||
['lit', 'lt'],
|
||||
['ltz', 'lb'],
|
||||
['lub', 'lu'],
|
||||
['lug', 'lg'],
|
||||
['mah', 'mh'],
|
||||
['mal', 'ml'],
|
||||
['mar', 'mr'],
|
||||
['mkd', 'mk'],
|
||||
['mlg', 'mg'],
|
||||
['mlt', 'mt'],
|
||||
['mon', 'mn'],
|
||||
['mri', 'mi'],
|
||||
['msa', 'ms'],
|
||||
['mya', 'my'],
|
||||
['nau', 'na'],
|
||||
['nav', 'nv'],
|
||||
['nbl', 'nr'],
|
||||
['nde', 'nd'],
|
||||
['ndo', 'ng'],
|
||||
['nep', 'ne'],
|
||||
['nld', 'nl'],
|
||||
['nno', 'nn'],
|
||||
['nob', 'nb'],
|
||||
['nor', 'no'],
|
||||
['nya', 'ny'],
|
||||
['oci', 'oc'],
|
||||
['oji', 'oj'],
|
||||
['ori', 'or'],
|
||||
['orm', 'om'],
|
||||
['oss', 'os'],
|
||||
['pan', 'pa'],
|
||||
['pli', 'pi'],
|
||||
['pol', 'pl'],
|
||||
['por', 'pt'],
|
||||
['pus', 'ps'],
|
||||
['que', 'qu'],
|
||||
['roh', 'rm'],
|
||||
['ron', 'ro'],
|
||||
['run', 'rn'],
|
||||
['rus', 'ru'],
|
||||
['sag', 'sg'],
|
||||
['san', 'sa'],
|
||||
['sin', 'si'],
|
||||
['slk', 'sk'],
|
||||
['slv', 'sl'],
|
||||
['sme', 'se'],
|
||||
['smo', 'sm'],
|
||||
['sna', 'sn'],
|
||||
['snd', 'sd'],
|
||||
['som', 'so'],
|
||||
['sot', 'st'],
|
||||
['spa', 'es'],
|
||||
['sqi', 'sq'],
|
||||
['srd', 'sc'],
|
||||
['srp', 'sr'],
|
||||
['ssw', 'ss'],
|
||||
['sun', 'su'],
|
||||
['swa', 'sw'],
|
||||
['swe', 'sv'],
|
||||
['tah', 'ty'],
|
||||
['tam', 'ta'],
|
||||
['tat', 'tt'],
|
||||
['tel', 'te'],
|
||||
['tgk', 'tg'],
|
||||
['tgl', 'tl'],
|
||||
['tha', 'th'],
|
||||
['tir', 'ti'],
|
||||
['ton', 'to'],
|
||||
['tsn', 'tn'],
|
||||
['tso', 'ts'],
|
||||
['tuk', 'tk'],
|
||||
['tur', 'tr'],
|
||||
['twi', 'tw'],
|
||||
['uig', 'ug'],
|
||||
['ukr', 'uk'],
|
||||
['urd', 'ur'],
|
||||
['uzb', 'uz'],
|
||||
['ven', 've'],
|
||||
['vie', 'vi'],
|
||||
['vol', 'vo'],
|
||||
['wln', 'wa'],
|
||||
['wol', 'wo'],
|
||||
['xho', 'xh'],
|
||||
['yid', 'yi'],
|
||||
['yor', 'yo'],
|
||||
['zha', 'za'],
|
||||
['zho', 'zh'],
|
||||
['zul', 'zu'],
|
||||
]);
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../config/tsconfig.base.json",
|
||||
"includes": [
|
||||
"*.ts",
|
||||
"**/*.ts",
|
||||
"*.js",
|
||||
"**/*.js"
|
||||
]
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
runtimeInputs,
|
||||
npmDepsHash,
|
||||
src,
|
||||
lib,
|
||||
buildNpmPackage,
|
||||
makeWrapper,
|
||||
nodejs_latest,
|
||||
jq,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) concatMapStringsSep;
|
||||
inherit (builtins) fromJSON readFile;
|
||||
|
||||
packageJSON = fromJSON (readFile "${src}/package.json");
|
||||
in
|
||||
buildNpmPackage rec {
|
||||
pname = packageJSON.name;
|
||||
inherit (packageJSON) version;
|
||||
|
||||
inherit src runtimeInputs npmDepsHash;
|
||||
|
||||
prePatch = ''
|
||||
mv ./tsconfig.json ./project.json
|
||||
sed 's/^ *\/\/.*//' ${../config/tsconfig.json} > ./base.json
|
||||
${jq}/bin/jq -sr '.[0] * .[1] | del(.extends)' ./project.json ./base.json > ./tsconfig.json
|
||||
rm base.json project.json
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [makeWrapper];
|
||||
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/${pname} \
|
||||
--prefix PATH : ${concatMapStringsSep ":" (p: p + "/bin") runtimeInputs}
|
||||
'';
|
||||
|
||||
nodejs = nodejs_latest;
|
||||
meta.mainProgram = pname;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
inherit (pkgs.lib) mapAttrs removeSuffix;
|
||||
in
|
||||
mapAttrs (
|
||||
name: app: (pkgs.symlinkJoin {
|
||||
name = "app-${name}";
|
||||
paths = [(removeSuffix "/bin/${name}" (toString app.program))];
|
||||
})
|
||||
)
|
||||
(removeAttrs self.apps.${pkgs.system} ["genflake"])
|
|
@ -1,2 +0,0 @@
|
|||
use flake $FLAKE#node
|
||||
npm ci
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
buildApp,
|
||||
callPackage,
|
||||
nix-update,
|
||||
nodejs_latest,
|
||||
prefetch-npm-deps,
|
||||
...
|
||||
}:
|
||||
buildApp {
|
||||
src = ./.;
|
||||
npmDepsHash = "sha256-vIhR/+Pgj35sv/ZX1iL+xWheu6w7vQeJy4OEfa8tXFw=";
|
||||
|
||||
runtimeInputs = [
|
||||
nix-update
|
||||
nodejs_latest
|
||||
prefetch-npm-deps
|
||||
(callPackage ../../modules/docker/updateImage.nix {})
|
||||
];
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import eslintConf from 'eslint-conf';
|
||||
|
||||
export default eslintConf;
|
BIN
apps/update-sources/package-lock.json
generated
BIN
apps/update-sources/package-lock.json
generated
Binary file not shown.
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"name": "update-sources",
|
||||
"version": "0.0.0",
|
||||
"bin": "out/bin/app.cjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node_ver=$(node -v); esbuild src/app.ts --bundle --platform=node --target=\"node${node_ver:1:2}\" --outfile=out/bin/app.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-conf": "file:../config",
|
||||
"@types/node": "22.10.2",
|
||||
"esbuild": "0.24.2",
|
||||
"eslint": "9.17.0",
|
||||
"jiti": "2.4.2",
|
||||
"pkg-types": "1.2.1",
|
||||
"typescript": "5.7.2"
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
import { spawnSync } from 'node:child_process';
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
import { parseArgs } from './lib';
|
||||
|
||||
import { updateDocker } from './docker';
|
||||
import { updateFirefoxAddons } from '././firefox';
|
||||
import { updateFlakeInputs } from './flake';
|
||||
import updateNodeModules from './node-modules';
|
||||
|
||||
import {
|
||||
runNixUpdate,
|
||||
updateCustomPackage,
|
||||
updateVuetorrent,
|
||||
} from './misc';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE;
|
||||
|
||||
if (!FLAKE) {
|
||||
console.error('Env var FLAKE not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const args = parseArgs();
|
||||
|
||||
const main = async() => {
|
||||
if (args['d'] || args['docker']) {
|
||||
console.log(updateDocker());
|
||||
}
|
||||
|
||||
if (args['i'] || args['inputs']) {
|
||||
console.log(updateFlakeInputs());
|
||||
}
|
||||
|
||||
if (args['f'] || args['firefox']) {
|
||||
console.log(updateFirefoxAddons());
|
||||
}
|
||||
|
||||
if (args['v'] || args['vuetorrent']) {
|
||||
console.log(updateVuetorrent());
|
||||
}
|
||||
|
||||
if (args['c'] || args['custom-sidebar']) {
|
||||
console.log(updateCustomPackage(
|
||||
'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar',
|
||||
));
|
||||
}
|
||||
|
||||
if (args['s'] || args['some-sass-language-server']) {
|
||||
console.log(updateCustomPackage('some-sass-language-server'));
|
||||
}
|
||||
|
||||
if (args['n'] || args['node_modules']) {
|
||||
console.log(await updateNodeModules());
|
||||
}
|
||||
|
||||
if (args['h'] || args['homepage']) {
|
||||
console.log(runNixUpdate('homepage'));
|
||||
}
|
||||
|
||||
if (args['a'] || args['all']) {
|
||||
// Update this first because of nix run cmd
|
||||
const firefoxOutput = updateFirefoxAddons();
|
||||
|
||||
console.log(firefoxOutput);
|
||||
|
||||
|
||||
const flakeOutput = updateFlakeInputs();
|
||||
|
||||
console.log(flakeOutput);
|
||||
|
||||
|
||||
const dockerOutput = updateDocker();
|
||||
|
||||
console.log(dockerOutput);
|
||||
|
||||
|
||||
const nodeModulesOutput = await updateNodeModules();
|
||||
|
||||
console.log(nodeModulesOutput);
|
||||
|
||||
|
||||
const vuetorrentOutput = updateVuetorrent();
|
||||
|
||||
console.log(vuetorrentOutput);
|
||||
|
||||
|
||||
// This doesn't need to be added to commit msgs
|
||||
console.log(updateCustomPackage(
|
||||
'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar',
|
||||
));
|
||||
console.log(updateCustomPackage('some-sass-language-server'));
|
||||
|
||||
// nix-update executions
|
||||
let nixUpdateOutputs = '';
|
||||
|
||||
const updatePackage = (pkg: string): void => {
|
||||
const execution = runNixUpdate(pkg);
|
||||
|
||||
nixUpdateOutputs += execution.stdout;
|
||||
console.log(execution.stderr);
|
||||
console.log(execution.stdout);
|
||||
};
|
||||
|
||||
updatePackage('homepage');
|
||||
|
||||
|
||||
spawnSync('nixFastBuild', [], {
|
||||
shell: true,
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
});
|
||||
|
||||
const indentOutput = (output: string): string => {
|
||||
return ` ${output.split('\n').join('\n ')}`;
|
||||
};
|
||||
|
||||
const output = [
|
||||
'chore: update sources\n\n',
|
||||
];
|
||||
|
||||
if (flakeOutput.length > 5) {
|
||||
output.push(`Flake Inputs:\n${indentOutput(flakeOutput)}\n\n`);
|
||||
}
|
||||
if (dockerOutput.length > 5) {
|
||||
output.push(`Docker Images:\n${indentOutput(dockerOutput)}\n`);
|
||||
}
|
||||
if (firefoxOutput.length > 5) {
|
||||
output.push(`Firefox Addons:\n${indentOutput(firefoxOutput)}\n\n`);
|
||||
}
|
||||
if (nodeModulesOutput.length > 5) {
|
||||
output.push(`Node modules:\n${indentOutput(nodeModulesOutput)}\n`);
|
||||
}
|
||||
if (vuetorrentOutput.length > 5) {
|
||||
output.push(`Misc Sources:\n${indentOutput(vuetorrentOutput)}\n\n`);
|
||||
}
|
||||
if (nixUpdateOutputs.length > 5) {
|
||||
output.push(`nix-update executions:\n${indentOutput(nixUpdateOutputs)}\n`);
|
||||
}
|
||||
|
||||
if (args['f']) {
|
||||
writeFileSync(args['f'] as string, output.join(''));
|
||||
}
|
||||
else {
|
||||
console.log(output.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
spawnSync('alejandra', ['-q', FLAKE], { shell: true });
|
||||
};
|
||||
|
||||
main();
|
|
@ -1,33 +0,0 @@
|
|||
import { readdirSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE;
|
||||
|
||||
const updateImages = (imagePath: string): string | undefined => {
|
||||
console.log(`Updating ${imagePath.split('/').at(-1)} images`);
|
||||
|
||||
const out = spawnSync('updateImages', [imagePath], { shell: true }).stdout.toString();
|
||||
|
||||
if (!out.startsWith('# Locked')) {
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateDocker = () => {
|
||||
let updates = '';
|
||||
|
||||
updates += updateImages(`${FLAKE}/configurations/nos/modules/jellyfin`) ?? '';
|
||||
updates += updateImages(`${FLAKE}/configurations/homie/modules/home-assistant/netdaemon`) ?? '';
|
||||
|
||||
const DIR = `${FLAKE}/configurations/nos/modules/docker`;
|
||||
|
||||
readdirSync(DIR, { withFileTypes: true, recursive: true }).forEach((path) => {
|
||||
if (path.name === 'compose.nix') {
|
||||
updates += updateImages(path.parentPath) ?? '';
|
||||
}
|
||||
});
|
||||
|
||||
return updates;
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE;
|
||||
|
||||
export const updateFirefoxAddons = () => {
|
||||
console.log('Updating firefox addons using mozilla-addons-to-nix');
|
||||
|
||||
const DIR = `${FLAKE}/scopedPackages/firefox-addons`;
|
||||
const GENERATED_FILE = `${DIR}/generated-firefox-addons.nix`;
|
||||
const SLUGS = `${DIR}/addons.json`;
|
||||
|
||||
const nameMap = Object.fromEntries([...JSON.parse(readFileSync(SLUGS, 'utf-8'))]
|
||||
.map((addon) => [addon.slug, addon.pname || addon.slug]));
|
||||
|
||||
const nixExpr = `'
|
||||
x: let
|
||||
inherit (builtins) attrValues filter hasAttr isAttrs map;
|
||||
in
|
||||
map (d: d.name) (filter (y:
|
||||
isAttrs y &&
|
||||
hasAttr "type" y &&
|
||||
y.type == "derivation") (attrValues x))
|
||||
'`;
|
||||
|
||||
const OLD_VERS = Object.fromEntries([...JSON.parse(spawnSync('nix', [
|
||||
'eval',
|
||||
'.#scopedPackages.x86_64-linux.firefoxAddons',
|
||||
'--apply',
|
||||
nixExpr,
|
||||
'--json',
|
||||
], { shell: true }).stdout.toString())]
|
||||
.map((p) => {
|
||||
const pname = p.replace(/-[0-9].*$/, '');
|
||||
|
||||
return [pname, p.replace(`${pname}-`, '')];
|
||||
})
|
||||
.filter((pinfo) => pinfo[0] !== 'frankerfacez'));
|
||||
|
||||
const NEW_VERS = Object.fromEntries(spawnSync(
|
||||
'nix',
|
||||
['run', 'sourcehut:~rycee/mozilla-addons-to-nix',
|
||||
SLUGS, GENERATED_FILE],
|
||||
{ shell: true },
|
||||
).stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((p) => {
|
||||
const pinfo = p.replace('Fetched ', '').split(' ');
|
||||
|
||||
return [nameMap[pinfo[0]], pinfo[2]];
|
||||
}));
|
||||
|
||||
return Object.keys(OLD_VERS)
|
||||
.sort()
|
||||
.filter((pname) => OLD_VERS[pname] !== NEW_VERS[pname])
|
||||
.map((pname) => `${pname}: ${OLD_VERS[pname]} -> ${NEW_VERS[pname]}`)
|
||||
.join('\n');
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE;
|
||||
|
||||
export const updateFlakeInputs = () => {
|
||||
const output = spawnSync(
|
||||
`git restore flake.lock &> /dev/null; nix flake update --flake ${FLAKE}` +
|
||||
' |& grep -v "warning: updating lock file" |& grep -v "unpacking"',
|
||||
[],
|
||||
{ shell: true },
|
||||
).stdout
|
||||
.toString()
|
||||
// Add an extra blank line between inputs
|
||||
.split('\n•')
|
||||
// Filter out some inputs
|
||||
.filter((input) => ![
|
||||
'systems',
|
||||
'flake-utils',
|
||||
'flake-parts',
|
||||
'treefmt-nix',
|
||||
'lib-aggregate',
|
||||
'lib-aggregate/nixpkgs-lib',
|
||||
'sops-nix/nixpkgs-stable',
|
||||
'discord-overlay/Vencord-src',
|
||||
'nix-gaming/umu',
|
||||
].some((inputName) => input.startsWith(` Updated input '${inputName}'`)))
|
||||
.join('\n\n•')
|
||||
// help readability of git revs
|
||||
.split('\n')
|
||||
.map((l) => l
|
||||
.replace(
|
||||
/\/(.{40})\?narHash=sha256[^']*(.*)/,
|
||||
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
|
||||
)
|
||||
.replace(
|
||||
/\?ref.*&rev=(.{40})[^'&]*(.*)/,
|
||||
(_, backref1, backref2) => `${backref2} rev: ${backref1}`,
|
||||
))
|
||||
.join('\n');
|
||||
|
||||
return output;
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
|
||||
export const parseArgs = () => {
|
||||
const args = {} as Record<string, unknown>;
|
||||
let lastFlag: string | null = null;
|
||||
|
||||
for (let i = 2; i < process.argv.length; ++i) {
|
||||
const arg = process.argv[i];
|
||||
|
||||
if (arg.toString().startsWith('-')) {
|
||||
lastFlag = arg.toString().replace(/^-{1,2}/, '');
|
||||
args[lastFlag] = true;
|
||||
}
|
||||
else if (lastFlag) {
|
||||
args[lastFlag] = arg;
|
||||
lastFlag = null;
|
||||
}
|
||||
else {
|
||||
console.error(`Could not parse args: ${arg.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
export const parseFetchurl = (url: string) => JSON.parse(spawnSync(
|
||||
'nix', ['store', 'prefetch-file', '--refresh', '--json',
|
||||
'--hash-type', 'sha256', url, '--name', '"escaped"'], { shell: true },
|
||||
).stdout.toString()).hash;
|
||||
|
||||
export const replaceInFile = (replace: RegExp, replacement: string, file: string) => {
|
||||
const fileContents = readFileSync(file);
|
||||
|
||||
const replaced = fileContents.toString().replace(replace, replacement);
|
||||
|
||||
writeFileSync(file, replaced);
|
||||
};
|
||||
|
||||
export const npmRun = (args: string[], workspaceDir: string) => spawnSync(
|
||||
'npm', args, { cwd: workspaceDir },
|
||||
).stdout.toString();
|
|
@ -1,73 +0,0 @@
|
|||
import { writeFileSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import { parseFetchurl } from './lib';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE;
|
||||
|
||||
const genVueText = (
|
||||
version: string,
|
||||
hash: string,
|
||||
url: string,
|
||||
) => `# This file was autogenerated. DO NOT EDIT!
|
||||
{
|
||||
version = "${version}";
|
||||
url = "${url}";
|
||||
hash = "${hash}";
|
||||
}
|
||||
`;
|
||||
|
||||
export const updateVuetorrent = () => {
|
||||
const FILE = `${FLAKE}/configurations/nos/modules/qbittorrent/vuetorrent.nix`;
|
||||
|
||||
const OLD_VERSION = JSON.parse(spawnSync('nix',
|
||||
['eval', '-f', FILE, '--json'],
|
||||
{ shell: true }).stdout.toString()).version;
|
||||
|
||||
const VERSION = JSON.parse(spawnSync('curl',
|
||||
['-s', 'https://api.github.com/repos/VueTorrent/VueTorrent/releases/latest'],
|
||||
{ shell: true }).stdout.toString()).tag_name.replace('v', '');
|
||||
|
||||
const URL = `https://github.com/VueTorrent/VueTorrent/releases/download/v${VERSION}/vuetorrent.zip`;
|
||||
const HASH = parseFetchurl(URL);
|
||||
|
||||
const fileText = genVueText(VERSION, HASH, URL);
|
||||
|
||||
writeFileSync(FILE, fileText);
|
||||
|
||||
return OLD_VERSION !== VERSION ? `Vuetorrent: ${OLD_VERSION} -> ${VERSION}` : '';
|
||||
};
|
||||
|
||||
|
||||
export const updateCustomPackage = (pkg: string) => spawnSync(
|
||||
`nix run ${FLAKE}#${pkg}.update`,
|
||||
[],
|
||||
{ shell: true },
|
||||
).stderr.toString();
|
||||
|
||||
|
||||
const getAttrVersion = (attr: string): string => spawnSync('nix',
|
||||
['eval', '--raw', `${FLAKE}#${attr}.version`],
|
||||
{ shell: true }).stdout.toString();
|
||||
|
||||
export const runNixUpdate = (
|
||||
attr: string,
|
||||
options: string[] = [],
|
||||
): { stdout: string, stderr: string } => {
|
||||
const OLD_VERSION = getAttrVersion(attr);
|
||||
|
||||
const execution = spawnSync(
|
||||
`nix-update --flake ${attr} --write-commit-message >(head -n 1 -) > /dev/null`,
|
||||
options,
|
||||
{ shell: true, cwd: FLAKE },
|
||||
);
|
||||
|
||||
const NEW_VERSION = getAttrVersion(attr);
|
||||
|
||||
return {
|
||||
stdout: OLD_VERSION !== NEW_VERSION ? execution.stdout.toString() : '',
|
||||
stderr: execution.stderr.toString(),
|
||||
};
|
||||
};
|
|
@ -1,82 +0,0 @@
|
|||
import { readPackageJSON, writePackageJSON } from 'pkg-types';
|
||||
import { readdirSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import { replaceInFile, npmRun } from './lib';
|
||||
|
||||
|
||||
/* Constants */
|
||||
const FLAKE = process.env.FLAKE as string;
|
||||
|
||||
|
||||
const updatePackageJson = async(workspaceDir: string, updates: object) => {
|
||||
const currentPackageJson = await readPackageJSON(`${workspaceDir}/package.json`);
|
||||
|
||||
const outdated = JSON.parse(npmRun(['outdated', '--json'], workspaceDir));
|
||||
|
||||
const updateDeps = (deps: string) => {
|
||||
Object.keys(currentPackageJson[deps]).forEach((dep) => {
|
||||
const versions = outdated[dep];
|
||||
const current = versions?.wanted || versions?.current;
|
||||
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current !== versions.latest) {
|
||||
updates[dep] = `${current} -> ${versions.latest}`;
|
||||
}
|
||||
|
||||
currentPackageJson[deps][dep] = versions.latest;
|
||||
});
|
||||
};
|
||||
|
||||
if (currentPackageJson.dependencies) {
|
||||
updateDeps('dependencies');
|
||||
}
|
||||
|
||||
if (currentPackageJson.devDependencies) {
|
||||
updateDeps('devDependencies');
|
||||
}
|
||||
|
||||
await writePackageJSON(`${workspaceDir}/package.json`, currentPackageJson);
|
||||
};
|
||||
|
||||
|
||||
const prefetchNpmDeps = (workspaceDir: string): string => {
|
||||
npmRun(['install', '--package-lock-only'], workspaceDir);
|
||||
|
||||
return spawnSync(
|
||||
'prefetch-npm-deps',
|
||||
[`${workspaceDir}/package-lock.json`],
|
||||
).stdout.toString().replace('\n', '');
|
||||
};
|
||||
|
||||
|
||||
export default async() => {
|
||||
const updates = {};
|
||||
|
||||
const packages = readdirSync(FLAKE, { withFileTypes: true, recursive: true });
|
||||
|
||||
for (const path of packages) {
|
||||
if (
|
||||
path.name === 'package.json' &&
|
||||
!path.parentPath.includes('node_modules') &&
|
||||
!path.parentPath.includes('apps/config')
|
||||
) {
|
||||
await updatePackageJson(path.parentPath, updates);
|
||||
|
||||
const hash = prefetchNpmDeps(path.parentPath);
|
||||
|
||||
replaceInFile(
|
||||
/npmDepsHash = ".*";/,
|
||||
`npmDepsHash = "${hash}";`,
|
||||
`${path.parentPath}/default.nix`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(updates)
|
||||
.map(([key, dep]) => `${key}: ${dep}`)
|
||||
.join('\n');
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../config/tsconfig.base.json",
|
||||
"includes": [
|
||||
"*.ts",
|
||||
"**/*.ts",
|
||||
"*.js",
|
||||
"**/*.js"
|
||||
]
|
||||
}
|
129
conf/pkgs.log
Normal file
129
conf/pkgs.log
Normal file
|
@ -0,0 +1,129 @@
|
|||
acpi
|
||||
alacritty
|
||||
asp
|
||||
bat
|
||||
blueman
|
||||
bluez
|
||||
bluez-plugins
|
||||
bluez-tools
|
||||
bluez-utils
|
||||
brightnessctl
|
||||
cliphist
|
||||
cpio
|
||||
cups
|
||||
discord
|
||||
doctest
|
||||
dolphin
|
||||
dosfstools
|
||||
exfatprogs
|
||||
f2fs-tools
|
||||
ffmpeg-compat-57
|
||||
ffmpegthumbs
|
||||
firefox
|
||||
flatery-icon-theme-git
|
||||
fwupd
|
||||
galaxybudsclient-bin
|
||||
gimp
|
||||
git-lfs
|
||||
gnome-bluetooth-3.0
|
||||
gnome-calculator
|
||||
gnome-keyring
|
||||
gnome-themes-extra
|
||||
gparted
|
||||
grim
|
||||
grub-customizer
|
||||
gtklock
|
||||
gtklock-playerctl-module
|
||||
gtklock-powerbar-module
|
||||
gtklock-userinfo-module
|
||||
hplip
|
||||
hyprpaper-git
|
||||
iio-sensor-proxy-git
|
||||
jdk-temurin
|
||||
kde-cli-tools
|
||||
kdegraphics-thumbnailers
|
||||
kimageformats
|
||||
kio-admin
|
||||
kvantum
|
||||
libinput-gestures-git
|
||||
libreoffice-fresh
|
||||
light
|
||||
lostfiles
|
||||
nano
|
||||
neofetch
|
||||
neovim-git
|
||||
network-manager-applet
|
||||
nextcloud-client
|
||||
nilfs-utils
|
||||
nm-connection-editor
|
||||
nwg-look-bin
|
||||
otf-font-awesome
|
||||
pavucontrol
|
||||
photoqt
|
||||
pkgfile
|
||||
pokemon-colorscripts-git
|
||||
polkit-kde-agent
|
||||
progress
|
||||
python-evdev
|
||||
python-libevdev
|
||||
python-pyclip
|
||||
python-pyqt5
|
||||
python-pyqt5-3d
|
||||
python-pyqt5-chart
|
||||
python-pyqt5-datavisualization
|
||||
python-pyqt5-networkauth
|
||||
python-pyqt5-purchasing
|
||||
python-pyqt5-webengine
|
||||
python-pyqt6
|
||||
python-pyqt6-3d
|
||||
python-pyqt6-charts
|
||||
python-pyqt6-datavisualization
|
||||
python-pyqt6-networkauth
|
||||
python-pyqt6-webengine
|
||||
python-uinput
|
||||
qt5-charts
|
||||
qt5-imageformats
|
||||
qt5ct
|
||||
reiserfsprogs
|
||||
ripgrep
|
||||
rofi-lbonn-wayland-only-git
|
||||
rustup
|
||||
sddm-git
|
||||
seahorse
|
||||
slurp
|
||||
spicetify-cli-git
|
||||
spicetify-themes-git
|
||||
spotify-snapstore
|
||||
spotifywm-git
|
||||
squeekboard-git
|
||||
swappy-git
|
||||
swayidle
|
||||
swaync-git
|
||||
swayosd-git
|
||||
tablet-mode
|
||||
tlp
|
||||
ttf-font-awesome
|
||||
ttf-go-nerd
|
||||
ttf-jetbrains-mono
|
||||
ttf-jetbrains-mono-nerd
|
||||
ttf-meslo-nerd-font-powerlevel10k
|
||||
ttf-ms-win11-auto
|
||||
ttf-nerd-fonts-symbols
|
||||
ttf-nerd-fonts-symbols-common
|
||||
ttf-space-mono-nerd
|
||||
ttf-ubuntu-font-family
|
||||
ttf-ubuntu-nerd
|
||||
tutanota-desktop-bin
|
||||
waybar-hyprland-git
|
||||
waydroid-git
|
||||
wev
|
||||
wine-staging
|
||||
winetricks
|
||||
wl-clip-persist-git
|
||||
wl-color-picker
|
||||
xclip
|
||||
xdg-desktop-portal-hyprland-git
|
||||
xdg-utils
|
||||
xorg-xwayland
|
||||
zathura-pdf-mupdf
|
||||
zenity
|
1
conf/toinstall.sh
Executable file
1
conf/toinstall.sh
Executable file
|
@ -0,0 +1 @@
|
|||
yay -Sy acpi alacritty asp bat blueman bluez bluez-plugins bluez-tools bluez-utils brightnessctl cliphist cpio cups discord doctest dolphin dosfstools exfatprogs f2fs-tools ffmpeg-compat-57 ffmpegthumbs firefox flatery-icon-theme-git fwupd galaxybudsclient-bin gimp git-lfs gnome-bluetooth-3.0 gnome-calculator gnome-keyring gnome-themes-extra gparted grim grub-customizer gtklock gtklock-playerctl-module gtklock-powerbar-module gtklock-userinfo-module hplip hyprpaper-git iio-sensor-proxy-git jdk-temurin kde-cli-tools kdegraphics-thumbnailers kimageformats kio-admin kvantum libinput-gestures-git libreoffice-fresh light lostfiles nano neofetch neovim-git network-manager-applet nextcloud-client nilfs-utils nm-connection-editor nwg-look-bin otf-font-awesome pavucontrol photoqt pkgfile pokemon-colorscripts-git polkit-kde-agent progress python-evdev python-libevdev python-pyclip python-pyqt5 python-pyqt5-3d python-pyqt5-chart python-pyqt5-datavisualization python-pyqt5-networkauth python-pyqt5-purchasing python-pyqt5-webengine python-pyqt6 python-pyqt6-3d python-pyqt6-charts python-pyqt6-datavisualization python-pyqt6-networkauth python-pyqt6-webengine python-uinput qt5-charts qt5-imageformats qt5ct reiserfsprogs ripgrep rofi-lbonn-wayland-only-git rustup sddm-git seahorse slurp spicetify-cli-git spicetify-themes-git spotify-snapstore spotifywm-git squeekboard-git swappy-git swayidle swaync-git swayosd-git tablet-mode tlp ttf-font-awesome ttf-go-nerd ttf-jetbrains-mono ttf-jetbrains-mono-nerd ttf-meslo-nerd-font-powerlevel10k ttf-ms-win11-auto ttf-nerd-fonts-symbols ttf-nerd-fonts-symbols-common ttf-space-mono-nerd ttf-ubuntu-font-family ttf-ubuntu-nerd tutanota-desktop-bin waybar-hyprland-git waydroid-git wev wine-staging winetricks wl-clip-persist-git wl-color-picker xclip xdg-desktop-portal-hyprland-git xdg-utils xorg-xwayland zathura-pdf-mupdf zenity
|
|
@ -1,16 +0,0 @@
|
|||
# Devices
|
||||
|
||||
This directory encompasses every device's main configuration file.
|
||||
|
||||
## List of my Devices
|
||||
|
||||
| Name | Description |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `android` | My [Nix-On-Droid](https://github.com/nix-community/nix-on-droid) configuration for my OnePlus 9 Pro |
|
||||
| `bbsteamie` | My wife's SteamDeck that has a pink case |
|
||||
| `binto` | My desktop PC with a multi-monitor setup and an NVIDIA (cringe) 3070 |
|
||||
| `cluster` | Two Lenovo mini PCs that make use of [NixOS-pcsd](https://github.com/matt1432/nixos-pcsd) to form a cluster |
|
||||
| `homie` | My Lenovo mini PC that will serve as a Home-assistant server |
|
||||
| `nos` | My custom built NAS |
|
||||
| `servivi` | A gaming PC in a previous life, it is now used as a build farm and hosts game servers |
|
||||
| `wim` | My 2-1 Lenovo Laptop that I use for uni |
|
|
@ -1,56 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) attrValues concatStringsSep;
|
||||
in {
|
||||
imports = [./nix-on-droid.nix];
|
||||
|
||||
environment.variables.FLAKE = "/data/data/com.termux.nix/files/home/.nix";
|
||||
|
||||
terminal.font = "${
|
||||
pkgs.nerd-fonts.jetbrains-mono
|
||||
}/share/fonts/truetype/NerdFonts/JetBrainsMono/JetBrainsMonoNerdFontMono-Regular.ttf";
|
||||
|
||||
environment.packages = [
|
||||
(pkgs.writeShellApplication {
|
||||
name = "switch";
|
||||
|
||||
runtimeInputs = attrValues {
|
||||
inherit
|
||||
(pkgs)
|
||||
coreutils
|
||||
nix-output-monitor
|
||||
nvd
|
||||
;
|
||||
};
|
||||
|
||||
text = ''
|
||||
oldProfile=$(realpath /nix/var/nix/profiles/per-user/nix-on-droid/profile)
|
||||
|
||||
nix-on-droid ${concatStringsSep " " [
|
||||
"switch"
|
||||
"--flake ${config.environment.variables.FLAKE}"
|
||||
"--builders ssh-ng://matt@100.64.0.7"
|
||||
''"$@"''
|
||||
"|&"
|
||||
"nom"
|
||||
]} &&
|
||||
|
||||
nvd diff "$oldProfile" "$(realpath /nix/var/nix/profiles/per-user/nix-on-droid/profile)"
|
||||
'';
|
||||
})
|
||||
];
|
||||
|
||||
environment.etcBackupExtension = ".bak";
|
||||
environment.motd = null;
|
||||
home-manager.backupFileExtension = "hm-bak";
|
||||
|
||||
# Set your time zone.
|
||||
time.timeZone = "America/Montreal";
|
||||
|
||||
# No touchy
|
||||
system.stateVersion = "23.05";
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
self.nixosModules.base-droid
|
||||
{
|
||||
roles.base = {
|
||||
enable = true;
|
||||
user = "nix-on-droid";
|
||||
};
|
||||
}
|
||||
|
||||
self.nixosModules.tmux
|
||||
{programs.tmux.enableCustomConf = true;}
|
||||
];
|
||||
|
||||
nix = {
|
||||
# Edit nix.conf
|
||||
extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
keep-outputs = true
|
||||
keep-derivations = true
|
||||
warn-dirty = false
|
||||
'';
|
||||
|
||||
substituters = [
|
||||
# Nix-community
|
||||
"https://nix-community.cachix.org"
|
||||
];
|
||||
trustedPublicKeys = [
|
||||
# Nix-community
|
||||
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
||||
];
|
||||
};
|
||||
|
||||
# Global hm settings
|
||||
home-manager.config = {
|
||||
imports = [
|
||||
self.homeManagerModules.neovim
|
||||
{
|
||||
programs.neovim = {
|
||||
enable = true;
|
||||
user = "nix-on-droid";
|
||||
};
|
||||
}
|
||||
|
||||
self.homeManagerModules.shell
|
||||
{programs.bash.enable = true;}
|
||||
|
||||
{
|
||||
programs.bash.sessionVariables = {
|
||||
FLAKE = config.environment.variables.FLAKE;
|
||||
};
|
||||
|
||||
programs.bash.shellAliases = {
|
||||
# Make ping work on nix-on-droid
|
||||
# https://github.com/nix-community/nix-on-droid/issues/185#issuecomment-1659294700
|
||||
ping = "/android/system/bin/linker64 /android/system/bin/ping";
|
||||
|
||||
# SSH
|
||||
# Desktop
|
||||
pc = "ssh -t matt@100.64.0.6 'tmux -2u new -At phone'";
|
||||
|
||||
# NAS
|
||||
nos = "ssh -t matt@100.64.0.4 'tmux -2u new -At phone'";
|
||||
|
||||
# Experimenting server
|
||||
servivi = "ssh -t matt@100.64.0.7 'tmux -2u new -At phone'";
|
||||
|
||||
# Home-assistant
|
||||
homie = "ssh -t matt@100.64.0.10 'tmux -2u new -At phone'";
|
||||
|
||||
# Cluster nodes
|
||||
thingone = "ssh -t matt@100.64.0.8 'tmux -2u new -At phone'";
|
||||
thingtwo = "ssh -t matt@100.64.0.9 'tmux -2u new -At phone'";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
home.stateVersion = "23.05";
|
||||
};
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
{
|
||||
mainUser,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
# ------------------------------------------------
|
||||
# Imports
|
||||
# ------------------------------------------------
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
|
||||
./modules
|
||||
|
||||
self.nixosModules.base
|
||||
self.nixosModules.kmscon
|
||||
self.nixosModules.plymouth
|
||||
self.nixosModules.server
|
||||
];
|
||||
|
||||
# State Version: DO NOT CHANGE
|
||||
system.stateVersion = "24.11";
|
||||
|
||||
# ------------------------------------------------
|
||||
# User Settings
|
||||
# ------------------------------------------------
|
||||
users.users.${mainUser} = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"adm"
|
||||
];
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = "bbsteamie";
|
||||
networkmanager.enable = true;
|
||||
};
|
||||
|
||||
time.timeZone = "America/Montreal";
|
||||
|
||||
# ------------------------------------------------
|
||||
# `Self` Modules configuration
|
||||
# ------------------------------------------------
|
||||
roles.base = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
user = mainUser;
|
||||
sshd.enable = true;
|
||||
};
|
||||
|
||||
boot.plymouth = {
|
||||
enable = true;
|
||||
theme = "bgrt";
|
||||
};
|
||||
|
||||
services.kmscon.enable = true;
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
imports = [
|
||||
self.homeManagerModules.shell
|
||||
];
|
||||
|
||||
programs = {
|
||||
bash = {
|
||||
enable = true;
|
||||
promptMainColor = "pink";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
config,
|
||||
jovian,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
nixpkgs.overlays = [jovian.overlays.default];
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
jovian.nixosModules.default
|
||||
];
|
||||
|
||||
jovian = {
|
||||
steamos.useSteamOSConfig = true;
|
||||
|
||||
devices.steamdeck = {
|
||||
enable = true;
|
||||
enableGyroDsuService = true;
|
||||
};
|
||||
hardware.has.amd.gpu = true;
|
||||
};
|
||||
|
||||
boot = {
|
||||
kernelModules = ["kvm-amd"];
|
||||
initrd.availableKernelModules = ["nvme" "xhci_pci" "usbhid" "sdhci_pci"];
|
||||
|
||||
loader = {
|
||||
efi.canTouchEfiVariables = true;
|
||||
|
||||
systemd-boot = {
|
||||
enable = true;
|
||||
configurationLimit = 30;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
virtualisation.waydroid.enable = true;
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-label/NIXBOOT";
|
||||
fsType = "vfat";
|
||||
options = ["fmask=0022" "dmask=0022"];
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/.swapfile";
|
||||
size = 16 * 1024; # 16GB
|
||||
}
|
||||
];
|
||||
|
||||
hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./desktop
|
||||
];
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
{pkgs, ...}: let
|
||||
defaultSession = "plasma";
|
||||
in {
|
||||
imports = [
|
||||
(import ./session-switching.nix defaultSession)
|
||||
(import ./steam.nix defaultSession)
|
||||
];
|
||||
|
||||
services.desktopManager.plasma6.enable = true;
|
||||
|
||||
programs = {
|
||||
kdeconnect.enable = true;
|
||||
xwayland.enable = true;
|
||||
};
|
||||
|
||||
# Flatpak support for Discover
|
||||
services.flatpak.enable = true;
|
||||
services.packagekit.enable = true;
|
||||
|
||||
# Wayland env vars
|
||||
environment.variables = {
|
||||
NIXOS_OZONE_WL = "1";
|
||||
ELECTRON_OZONE_PLATFORM_HINT = "auto";
|
||||
};
|
||||
|
||||
environment.systemPackages = builtins.attrValues {
|
||||
inherit
|
||||
(pkgs)
|
||||
firefox
|
||||
wl-clipboard
|
||||
xclip
|
||||
;
|
||||
|
||||
inherit
|
||||
(pkgs.kdePackages)
|
||||
discover
|
||||
krfb
|
||||
;
|
||||
};
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
# How to install Palia Map and Overwolf on Linux
|
||||
|
||||
Dependencies:
|
||||
- latest GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom
|
||||
- protontricks: https://github.com/Matoking/protontricks
|
||||
- protonhax: https://github.com/jcnils/protonhax
|
||||
|
||||
|
||||
## First Step: Install and run Palia at least once
|
||||
|
||||
|
||||
## Second Step: Setup Palia WINEPREFIX with latest GE-Proton
|
||||
|
||||
1. Delete prefix at /home/"$USER"/.steam/steam/steamapps/compatdata/2707930/pfx
|
||||
|
||||
```bash
|
||||
rm -r /home/"$USER"/.steam/steam/steamapps/compatdata/2707930/pfx
|
||||
```
|
||||
|
||||
2. Launch Palia with GE-Proton by going to game properties -> Compatibility -> Force the use ... -> Select GE-Proton...
|
||||
|
||||
3. Install dotnet48 and other Windows deps to allow for Overwolf installation
|
||||
|
||||
```bash
|
||||
# Force proton version of protontricks
|
||||
export PROTON_VERSION="GE-Proton9-10"
|
||||
protontricks 2707930 dotnet48
|
||||
|
||||
# If VC is needed
|
||||
protontricks 2707930 vcrun2010
|
||||
protontricks 2707930 vcrun2012
|
||||
protontricks 2707930 vcrun2013
|
||||
```
|
||||
|
||||
|
||||
## Third Step: Install Overwolf
|
||||
|
||||
1. Get this older version that worked for me here: https://overwolf.en.uptodown.com/windows/download/4714215
|
||||
|
||||
2. Install it with protontricks. Follow the GUI installer as if you were on Windows
|
||||
|
||||
```bash
|
||||
export PROTON_VERSION="GE-Proton9-10"
|
||||
protontricks-launch --appid 2707930 Downloads/overwolf-0-195-0-18.exe
|
||||
```
|
||||
|
||||
|
||||
## Fourth Step: Install Palia Map
|
||||
|
||||
1. Get the installer from here: https://www.overwolf.com/app/Leon_Machens-Palia_Map
|
||||
|
||||
2. Install it with protontricks.
|
||||
|
||||
```bash
|
||||
export PROTON_VERSION="GE-Proton9-10"
|
||||
protontricks-launch --appid 2707930 Downloads/Palia\ Map\ -\ Installer.exe
|
||||
```
|
||||
|
||||
3. An error should popup saying `Installation failed` or something along those lines.
|
||||
Close it and wait. You should see the Overwolf overlay on the left side of your screen
|
||||
with the Palia Map icon saying `Installing` and then when it's done: `Palia Map`.
|
||||
|
||||
If nothing happens, try rebooting your PC and retrying the above shell commands until it works.
|
||||
|
||||
|
||||
## Final Step: Setting it up in Steam
|
||||
|
||||
1. Add this line to Palia launch options
|
||||
```bash
|
||||
protonhax init %command%
|
||||
```
|
||||
|
||||
2. Add random non-steam game to your library and then edit its properties:
|
||||
|
||||
Change the name to `Palia Map` or anything you like
|
||||
|
||||
Change the target to this command:
|
||||
|
||||
```bash
|
||||
protonhax run 2707930 "/home/$USER/.local/share/Steam/steamapps/compatdata/2707930/pfx/drive_c/users/steamuser/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Overwolf/Palia Map.lnk"
|
||||
```
|
|
@ -1,114 +0,0 @@
|
|||
defaultSession: {
|
||||
config,
|
||||
lib,
|
||||
mainUser,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
config = let
|
||||
inherit (lib) findFirst getExe mkForce;
|
||||
|
||||
switch-session = pkgs.writeShellApplication {
|
||||
name = "switch-session";
|
||||
|
||||
text = ''
|
||||
mkdir -p /etc/sddm.conf.d
|
||||
|
||||
cat <<EOF | tee /etc/sddm.conf.d/autologin.conf
|
||||
[Autologin]
|
||||
User=${mainUser}
|
||||
Session=$1
|
||||
Relogin=true
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
|
||||
gaming-mode = pkgs.writeShellScriptBin "gaming-mode" ''
|
||||
sudo ${pkgs.systemd}/bin/systemctl start to-gaming-mode.service
|
||||
'';
|
||||
in {
|
||||
services.displayManager.sddm = {
|
||||
enable = true;
|
||||
autoLogin.relogin = true;
|
||||
|
||||
wayland = {
|
||||
enable = true;
|
||||
compositorCommand = "kwin";
|
||||
};
|
||||
};
|
||||
|
||||
# Sets the default session at launch
|
||||
systemd.services."set-session" = {
|
||||
wantedBy = ["multi-user.target"];
|
||||
before = ["display-manager.service"];
|
||||
|
||||
path = [switch-session];
|
||||
|
||||
script = ''
|
||||
switch-session "${defaultSession}"
|
||||
'';
|
||||
};
|
||||
|
||||
# Allows switching to gaming mode
|
||||
systemd.services."to-gaming-mode" = {
|
||||
wantedBy = mkForce [];
|
||||
|
||||
path = [switch-session];
|
||||
|
||||
script = ''
|
||||
switch-session "gamescope-wayland"
|
||||
systemctl restart display-manager
|
||||
sleep 10
|
||||
switch-session "${defaultSession}"
|
||||
'';
|
||||
};
|
||||
|
||||
# Make it so we don't need root to switch to gaming mode
|
||||
security.sudo.extraRules = [
|
||||
{
|
||||
users = [mainUser];
|
||||
groups = [100];
|
||||
commands = [
|
||||
{
|
||||
command = "${pkgs.systemd}/bin/systemctl start to-gaming-mode.service";
|
||||
options = ["SETENV" "NOPASSWD"];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
# Add desktop entry to make it GUI friendly
|
||||
xdg.desktopEntries."Gaming Mode" = {
|
||||
name = "Gaming Mode";
|
||||
exec = getExe gaming-mode;
|
||||
icon = "steam";
|
||||
terminal = false;
|
||||
type = "Application";
|
||||
};
|
||||
|
||||
home.file."Desktop/Gaming Mode.desktop".source =
|
||||
(
|
||||
findFirst
|
||||
(x: x.meta.name == "Gaming Mode.desktop") {}
|
||||
config.home-manager.users.mariah.home.packages
|
||||
)
|
||||
+ "/share/applications/Gaming Mode.desktop";
|
||||
|
||||
# Fix remote control prompt showing up everytime
|
||||
xdg.configFile = let
|
||||
mkAutostart = name: flags: {
|
||||
"autostart/${name}.desktop".text = "[Desktop Entry]\nType=Application\nExec=${name} ${flags}";
|
||||
};
|
||||
in (
|
||||
# Needs xdg-desktop-portal-kde patch provided by `self.overlays.xdg-desktop-portal-kde`
|
||||
{"plasmaremotedesktoprc".text = "[Sharing]\nUnattended=true";}
|
||||
// (mkAutostart "krfb" "--nodialog %c")
|
||||
// (mkAutostart "steam" "-silent %U")
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
# For accurate stack trace
|
||||
_file = ./session-switching.nix;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
defaultSession: {
|
||||
config,
|
||||
lib,
|
||||
mainUser,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
config = {
|
||||
# Normal Steam Stuff
|
||||
programs.steam = {
|
||||
enable = true;
|
||||
protontricks.enable = true;
|
||||
|
||||
remotePlay.openFirewall = true;
|
||||
extraCompatPackages = [
|
||||
self.packages.${pkgs.system}.proton-ge-latest
|
||||
];
|
||||
};
|
||||
|
||||
# Jovian Steam settings
|
||||
jovian.steam = {
|
||||
# Steam > Settings > System > Enable Developer Mode
|
||||
# Steam > Developer > CEF Remote Debugging
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
|
||||
environment = {
|
||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS =
|
||||
lib.makeSearchPathOutput
|
||||
"steamcompattool"
|
||||
""
|
||||
config.programs.steam.extraCompatPackages;
|
||||
};
|
||||
|
||||
desktopSession = defaultSession;
|
||||
};
|
||||
|
||||
# Decky settings
|
||||
jovian.decky-loader = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
stateDir = "/home/${mainUser}/.local/share/decky"; # Keep scoped to user
|
||||
# https://github.com/Jovian-Experiments/Jovian-NixOS/blob/1171169117f63f1de9ef2ea36efd8dcf377c6d5a/modules/decky-loader.nix#L80-L84
|
||||
|
||||
extraPackages = builtins.attrValues {
|
||||
inherit
|
||||
(pkgs)
|
||||
curl
|
||||
unzip
|
||||
util-linux
|
||||
gnugrep
|
||||
readline
|
||||
procps
|
||||
pciutils
|
||||
libpulseaudio
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
# Misc Packages
|
||||
environment.systemPackages = [
|
||||
pkgs.steam-rom-manager
|
||||
pkgs.r2modman
|
||||
|
||||
self.packages.${pkgs.system}.protonhax
|
||||
|
||||
# Ryujinx ACNH crashes on Vulkan
|
||||
pkgs.ryujinx
|
||||
];
|
||||
};
|
||||
|
||||
# For accurate stack trace
|
||||
_file = ./steam.nix;
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
{
|
||||
mainUser,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
# ------------------------------------------------
|
||||
# Imports
|
||||
# ------------------------------------------------
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
|
||||
./modules
|
||||
|
||||
self.nixosModules.base
|
||||
self.nixosModules.desktop
|
||||
self.nixosModules.kmscon
|
||||
self.nixosModules.server
|
||||
];
|
||||
|
||||
# State Version: DO NOT CHANGE
|
||||
system.stateVersion = "23.11";
|
||||
|
||||
# ------------------------------------------------
|
||||
# User Settings
|
||||
# ------------------------------------------------
|
||||
users.users.${mainUser} = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"input"
|
||||
"uinput"
|
||||
"adm"
|
||||
"video"
|
||||
"libvirtd"
|
||||
"adbusers"
|
||||
];
|
||||
};
|
||||
|
||||
programs.adb.enable = true;
|
||||
|
||||
networking = {
|
||||
hostName = "binto";
|
||||
networkmanager.enable = true;
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
time.timeZone = "America/Montreal";
|
||||
|
||||
# ------------------------------------------------
|
||||
# `Self` Modules configuration
|
||||
# ------------------------------------------------
|
||||
roles.base = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
|
||||
roles.desktop = {
|
||||
user = mainUser;
|
||||
|
||||
ags.enable = true;
|
||||
mainMonitor = "desc:GIGA-BYTE TECHNOLOGY CO. LTD. G27QC 0x00000B1D";
|
||||
displayManager.duplicateScreen = false;
|
||||
|
||||
fontSize = 12.5;
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
user = mainUser;
|
||||
tailscale.enable = true;
|
||||
sshd.enable = true;
|
||||
};
|
||||
|
||||
services.kmscon.enable = true;
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
imports = [
|
||||
self.homeManagerModules.firefox
|
||||
self.homeManagerModules.neovim
|
||||
self.homeManagerModules.shell
|
||||
];
|
||||
|
||||
programs = {
|
||||
bash = {
|
||||
enable = true;
|
||||
promptMainColor = "purple";
|
||||
};
|
||||
|
||||
neovim = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
{
|
||||
config,
|
||||
modulesPath,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
|
||||
|
||||
boot = {
|
||||
kernelPackages = pkgs.linuxPackages_zen;
|
||||
|
||||
kernelParams = [
|
||||
"amd_pstate=active"
|
||||
|
||||
# Remove these if I use plymouth module
|
||||
"quiet"
|
||||
"splash"
|
||||
"boot.shell_on_fail"
|
||||
"i915.fastboot=1"
|
||||
"loglevel=3"
|
||||
"rd.systemd.show_status=false"
|
||||
"rd.udev.log_level=3"
|
||||
"udev.log_priority=3"
|
||||
];
|
||||
kernelModules = ["kvm-amd"];
|
||||
|
||||
# Zenpower for ryzen cpu monitoring
|
||||
extraModulePackages = builtins.attrValues {
|
||||
inherit
|
||||
(config.boot.kernelPackages)
|
||||
v4l2loopback
|
||||
zenpower
|
||||
;
|
||||
};
|
||||
blacklistedKernelModules = ["k10temp"];
|
||||
|
||||
supportedFilesystems = ["ntfs"];
|
||||
|
||||
consoleLogLevel = 0;
|
||||
|
||||
initrd = {
|
||||
verbose = false;
|
||||
systemd.enable = true;
|
||||
availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"];
|
||||
};
|
||||
|
||||
loader = {
|
||||
efi.canTouchEfiVariables = true;
|
||||
timeout = 0;
|
||||
|
||||
systemd-boot = {
|
||||
enable = true;
|
||||
consoleMode = "max";
|
||||
configurationLimit = 30;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
};
|
||||
|
||||
# sudo btrfs subvolume create /@swap
|
||||
"/swap" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
# Idk why this is the subvol
|
||||
options = ["subvol=@/@swap"];
|
||||
};
|
||||
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-label/NIXBOOT";
|
||||
fsType = "vfat";
|
||||
};
|
||||
|
||||
"/run/media/matt/Games" = {
|
||||
device = "/dev/disk/by-uuid/da62f4ee-d4a6-4fdd-ab12-9c5e131c6f30";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/swap/swapfile";
|
||||
size = 16 * 1024;
|
||||
}
|
||||
];
|
||||
|
||||
zramSwap.enable = true;
|
||||
|
||||
hardware = {
|
||||
cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||
uinput.enable = true;
|
||||
};
|
||||
|
||||
virtualisation = {
|
||||
libvirtd.enable = true;
|
||||
spiceUSBRedirection.enable = true;
|
||||
};
|
||||
environment.systemPackages = builtins.attrValues {
|
||||
inherit
|
||||
(pkgs)
|
||||
qemu
|
||||
virtiofsd
|
||||
;
|
||||
};
|
||||
|
||||
nvidia = {
|
||||
enable = true;
|
||||
enableNvidiaSettings = true;
|
||||
enableWayland = true;
|
||||
enableCUDA = true;
|
||||
};
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./gpu-replay.nix
|
||||
./nix-gaming.nix
|
||||
];
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
mainUser,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) concatStringsSep getExe removePrefix;
|
||||
inherit (self.packages.${pkgs.system}) gpu-screen-recorder gsr-kms-server;
|
||||
|
||||
hyprPkgs = config.home-manager.users.${mainUser}.wayland.windowManager.hyprland.finalPackage;
|
||||
|
||||
cfgDesktop = config.roles.desktop;
|
||||
in {
|
||||
security.wrappers = {
|
||||
gpu-screen-recorder = {
|
||||
owner = "root";
|
||||
group = "video";
|
||||
capabilities = "cap_sys_nice+ep";
|
||||
source = getExe gpu-screen-recorder;
|
||||
};
|
||||
|
||||
gsr-kms-server = {
|
||||
owner = "root";
|
||||
group = "video";
|
||||
capabilities = "cap_sys_admin+ep";
|
||||
source = getExe gsr-kms-server;
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
home.packages = [
|
||||
(pkgs.writeShellApplication {
|
||||
name = "gpu-save-replay";
|
||||
|
||||
runtimeInputs = with pkgs; [procps];
|
||||
|
||||
text = ''
|
||||
pkill --signal SIGUSR1 -f gpu-screen-recorder
|
||||
'';
|
||||
})
|
||||
|
||||
(pkgs.writeShellApplication {
|
||||
name = "gsr-start";
|
||||
|
||||
runtimeInputs = [
|
||||
pkgs.pulseaudio
|
||||
pkgs.xorg.xrandr
|
||||
|
||||
hyprPkgs
|
||||
];
|
||||
|
||||
text = ''
|
||||
main="${removePrefix "desc:" cfgDesktop.mainMonitor}"
|
||||
WINDOW=$(hyprctl -j monitors | jq '.[] |= (.description |= gsub(","; ""))' | jq -r ".[] | select(.description | test(\"$main\")) | .name")
|
||||
|
||||
# Fix fullscreen game resolution
|
||||
xrandr --output "$WINDOW" --primary
|
||||
|
||||
gpu-screen-recorder ${concatStringsSep " " [
|
||||
''-v no''
|
||||
''-r 1200''
|
||||
''-df yes''
|
||||
''-o /home/matt/Videos/Replay''
|
||||
# Audio settings
|
||||
''-ac aac''
|
||||
''-a "$(pactl get-default-sink).monitor"''
|
||||
''-a "$(pactl get-default-source)"''
|
||||
# Video settings
|
||||
''-w "$WINDOW"''
|
||||
''-f 60''
|
||||
''-c mkv''
|
||||
''-k hevc''
|
||||
''-q very_high''
|
||||
]}
|
||||
'';
|
||||
})
|
||||
];
|
||||
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
bind = [",F8, exec, ags request 'save-replay'"];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
nix-gaming,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
nix-gaming.nixosModules.platformOptimizations
|
||||
];
|
||||
|
||||
programs = {
|
||||
steam = {
|
||||
enable = true;
|
||||
protontricks.enable = true;
|
||||
remotePlay.openFirewall = true;
|
||||
|
||||
extraCompatPackages = [
|
||||
self.packages.${pkgs.system}.proton-ge-latest
|
||||
];
|
||||
|
||||
platformOptimizations.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.lutris.override {
|
||||
extraLibraries = pkgs: [
|
||||
# List library dependencies here
|
||||
];
|
||||
extraPkgs = pkgs: [
|
||||
# List extra packages available to lutris here
|
||||
];
|
||||
})
|
||||
|
||||
pkgs.r2modman
|
||||
pkgs.ryujinx
|
||||
];
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
deviceName: {
|
||||
config,
|
||||
mainUser,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip;
|
||||
in {
|
||||
# ------------------------------------------------
|
||||
# Imports
|
||||
# ------------------------------------------------
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
|
||||
./modules
|
||||
|
||||
self.nixosModules.base
|
||||
self.nixosModules.kmscon
|
||||
self.nixosModules.server
|
||||
];
|
||||
|
||||
# State Version: DO NOT CHANGE
|
||||
system.stateVersion = "24.05";
|
||||
|
||||
# ------------------------------------------------
|
||||
# User Settings
|
||||
# ------------------------------------------------
|
||||
users.users.${mainUser} = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"adm"
|
||||
];
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = deviceName;
|
||||
resolvconf.enable = true;
|
||||
nameservers = [
|
||||
clusterIP
|
||||
"1.0.0.1"
|
||||
];
|
||||
extraHosts = ''
|
||||
10.0.0.244 thingone
|
||||
10.0.0.159 thingtwo
|
||||
'';
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
time.timeZone = "America/Montreal";
|
||||
|
||||
# ------------------------------------------------
|
||||
# `Self` Modules configuration
|
||||
# ------------------------------------------------
|
||||
roles.base = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
user = mainUser;
|
||||
tailscale.enable = true;
|
||||
sshd.enable = true;
|
||||
};
|
||||
|
||||
services.kmscon.enable = true;
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
imports = [
|
||||
self.homeManagerModules.neovim
|
||||
self.homeManagerModules.shell
|
||||
];
|
||||
|
||||
programs = {
|
||||
bash = {
|
||||
enable = true;
|
||||
promptMainColor =
|
||||
if deviceName == "thingone"
|
||||
then "green"
|
||||
else if deviceName == "thingtwo"
|
||||
then "red"
|
||||
else "purple";
|
||||
};
|
||||
|
||||
neovim = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
config,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
|
||||
|
||||
boot = {
|
||||
loader = {
|
||||
efi.canTouchEfiVariables = true;
|
||||
timeout = 2;
|
||||
|
||||
systemd-boot = {
|
||||
enable = true;
|
||||
consoleMode = "max";
|
||||
configurationLimit = 30;
|
||||
};
|
||||
};
|
||||
|
||||
initrd.availableKernelModules = [
|
||||
"xhci_pci"
|
||||
"ahci"
|
||||
"usbhid"
|
||||
"usb_storage"
|
||||
"sd_mod"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
};
|
||||
|
||||
# sudo btrfs subvolume create /@swap
|
||||
"/swap" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
options = ["subvol=@swap"];
|
||||
};
|
||||
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-label/NIXBOOT";
|
||||
fsType = "vfat";
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/swap/swapfile";
|
||||
size = 16 * 1024;
|
||||
}
|
||||
];
|
||||
|
||||
zramSwap.enable = true;
|
||||
|
||||
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{...}: {
|
||||
services = {
|
||||
blocky = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
upstream = {
|
||||
default = [
|
||||
"127.0.0.1:5335"
|
||||
"127.0.0.1:5335"
|
||||
];
|
||||
};
|
||||
|
||||
blocking = {
|
||||
blackLists = {
|
||||
ads = [
|
||||
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
{
|
||||
caddy-plugins,
|
||||
config,
|
||||
mainUser,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (config.sops) secrets;
|
||||
inherit (config.networking) hostName;
|
||||
|
||||
caddy = caddy-plugins.packages.${pkgs.system}.default;
|
||||
in {
|
||||
imports = [caddy-plugins.nixosModules.default];
|
||||
|
||||
# User stuff
|
||||
environment.systemPackages = [caddy];
|
||||
users.users.${mainUser}.extraGroups = ["caddy"];
|
||||
|
||||
boot.kernel.sysctl."net.ipv4.ip_nonlocal_bind" = 1;
|
||||
|
||||
systemd.services.caddy.serviceConfig = {
|
||||
EnvironmentFile = secrets.caddy-cloudflare.path;
|
||||
|
||||
# For some reason the service
|
||||
# doesn't shutdown normally
|
||||
KillSignal = "SIGKILL";
|
||||
RestartKillSignal = "SIGKILL";
|
||||
};
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
enableReload = false;
|
||||
package = caddy;
|
||||
|
||||
virtualHosts = let
|
||||
clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip;
|
||||
nosIP = "10.0.0.121";
|
||||
serviviIP = "10.0.0.249";
|
||||
homieIP = "100.64.0.10";
|
||||
|
||||
tlsConf = ''
|
||||
tls {
|
||||
dns cloudflare {$CLOUDFLARE_API_TOKEN}
|
||||
resolvers 1.0.0.1
|
||||
}
|
||||
'';
|
||||
|
||||
mkPublicReverseProxy = subdomain: ip: extraConf:
|
||||
{
|
||||
hostName = "${subdomain}.nelim.org";
|
||||
reverseProxy = ip;
|
||||
listenAddresses = [clusterIP];
|
||||
extraConfig = tlsConf + (extraConf.extraConfig or "");
|
||||
}
|
||||
// (builtins.removeAttrs extraConf ["extraConfig"]);
|
||||
in {
|
||||
# Public
|
||||
"Home-Assistant" = mkPublicReverseProxy "homie" "${homieIP}:8123" {};
|
||||
"Vaultwarden" = mkPublicReverseProxy "vault" "${nosIP}:8781" {};
|
||||
"Hauk" = mkPublicReverseProxy "hauk" "${nosIP}:3003" {};
|
||||
"Headscale" = mkPublicReverseProxy "headscale" "${clusterIP}:8085" {};
|
||||
|
||||
"Jellyfin" = mkPublicReverseProxy "jelly" "${nosIP}:8096" {
|
||||
subDirectories.jfa-go = {
|
||||
subDirName = "accounts";
|
||||
reverseProxy = "${nosIP}:8056";
|
||||
};
|
||||
};
|
||||
|
||||
"Jellyseer" = mkPublicReverseProxy "seerr" "${nosIP}:5055" {};
|
||||
|
||||
"Gameyfin" = mkPublicReverseProxy "games" "${nosIP}:8074" {};
|
||||
|
||||
"Forgejo" = mkPublicReverseProxy "git" "${nosIP}:3000" {};
|
||||
|
||||
"Nextcloud" = mkPublicReverseProxy "cloud" "${nosIP}:8042" {
|
||||
extraConfig = ''
|
||||
redir /.well-known/carddav /remote.php/dav 301
|
||||
redir /.well-known/caldav /remote.php/dav 301
|
||||
redir /.well-known/webfinger /index.php/.well-known/webfinger 301
|
||||
redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301
|
||||
'';
|
||||
};
|
||||
"OnlyOffice" = mkPublicReverseProxy "office" "http://${nosIP}:8055" {};
|
||||
|
||||
"Immich" = mkPublicReverseProxy "photos" "${nosIP}:2283" {};
|
||||
|
||||
"Binary Cache" = mkPublicReverseProxy "cache" "${serviviIP}:5000" {};
|
||||
"Apt Binary Cache" = mkPublicReverseProxy "cache-apt" "${homieIP}:5000" {};
|
||||
|
||||
# Private
|
||||
"nelim.org" = {
|
||||
serverAliases = ["*.nelim.org"];
|
||||
extraConfig = tlsConf;
|
||||
listenAddresses = [
|
||||
(
|
||||
if hostName == "thingone"
|
||||
then "100.64.0.8"
|
||||
else "100.64.0.9"
|
||||
)
|
||||
];
|
||||
|
||||
subDomains = {
|
||||
esphome.reverseProxy = "${homieIP}:6052";
|
||||
pr-tracker.reverseProxy = "${serviviIP}:3000";
|
||||
|
||||
pcsd = {
|
||||
extraConfig = ''
|
||||
reverse_proxy https://${clusterIP}:2224 {
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# Resume builder
|
||||
resume.reverseProxy = "${nosIP}:3060";
|
||||
resauth.reverseProxy = "${nosIP}:3100";
|
||||
|
||||
# FreshRSS & Co
|
||||
bridge.reverseProxy = "${nosIP}:3006";
|
||||
drss.reverseProxy = "${nosIP}:3007";
|
||||
freshrss = {
|
||||
subDomainName = "rss";
|
||||
reverseProxy = "${nosIP}:2800";
|
||||
};
|
||||
|
||||
wgui.reverseProxy = "${nosIP}:51821";
|
||||
|
||||
lan = {
|
||||
reverseProxy = "${nosIP}:3020";
|
||||
extraConfig = ''
|
||||
redir /index.html /
|
||||
'';
|
||||
|
||||
subDirectories = {
|
||||
bazarr.reverseProxy = "${nosIP}:6767";
|
||||
prowlarr.reverseProxy = "${nosIP}:9696";
|
||||
radarr.reverseProxy = "${nosIP}:7878";
|
||||
sabnzbd.reverseProxy = "${nosIP}:8382";
|
||||
sonarr.reverseProxy = "${nosIP}:8989";
|
||||
|
||||
qbittorent = {
|
||||
subDirName = "qbt";
|
||||
experimental = true;
|
||||
reverseProxy = "${nosIP}:8080";
|
||||
};
|
||||
|
||||
vaultwarden = {
|
||||
subDirName = "vault";
|
||||
experimental = true;
|
||||
reverseProxy = "${nosIP}:8780";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Top secret Business
|
||||
joal.extraConfig = ''
|
||||
route {
|
||||
rewrite * /joal/ui{uri}
|
||||
reverse_proxy * ${nosIP}:5656
|
||||
}
|
||||
'';
|
||||
joalws.extraConfig = ''
|
||||
route {
|
||||
reverse_proxy ${nosIP}:5656
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./blocky.nix
|
||||
./caddy.nix
|
||||
./headscale
|
||||
./nfs-client.nix
|
||||
./pcsd.nix
|
||||
./unbound.nix
|
||||
];
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
# bash completion V2 for headscale -*- shell-script -*-
|
||||
|
||||
__headscale_debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then
|
||||
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Macs have bash3 for which the bash-completion package doesn't include
|
||||
# _init_completion. This is a minimal version of that function.
|
||||
__headscale_init_completion()
|
||||
{
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref "$@" cur prev words cword
|
||||
}
|
||||
|
||||
# This function calls the headscale program to obtain the completion
|
||||
# results and the directive. It fills the 'out' and 'directive' vars.
|
||||
__headscale_get_completion_results() {
|
||||
local requestComp lastParam lastChar args
|
||||
|
||||
# Prepare the command to request completions for the program.
|
||||
# Calling ${words[0]} instead of directly headscale allows handling aliases
|
||||
args=("${words[@]:1}")
|
||||
requestComp="${words[0]} __complete ${args[*]}"
|
||||
|
||||
lastParam=${words[$((${#words[@]}-1))]}
|
||||
lastChar=${lastParam:$((${#lastParam}-1)):1}
|
||||
__headscale_debug "lastParam ${lastParam}, lastChar ${lastChar}"
|
||||
|
||||
if [[ -z ${cur} && ${lastChar} != = ]]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go method.
|
||||
__headscale_debug "Adding extra empty parameter"
|
||||
requestComp="${requestComp} ''"
|
||||
fi
|
||||
|
||||
# When completing a flag with an = (e.g., headscale -n=<TAB>)
|
||||
# bash focuses on the part after the =, so we need to remove
|
||||
# the flag part from $cur
|
||||
if [[ ${cur} == -*=* ]]; then
|
||||
cur="${cur#*=}"
|
||||
fi
|
||||
|
||||
__headscale_debug "Calling ${requestComp}"
|
||||
# Use eval to handle any environment variables and such
|
||||
out=$(eval "${requestComp}" 2>/dev/null)
|
||||
|
||||
# Extract the directive integer at the very end of the output following a colon (:)
|
||||
directive=${out##*:}
|
||||
# Remove the directive
|
||||
out=${out%:*}
|
||||
if [[ ${directive} == "${out}" ]]; then
|
||||
# There is not directive specified
|
||||
directive=0
|
||||
fi
|
||||
__headscale_debug "The completion directive is: ${directive}"
|
||||
__headscale_debug "The completions are: ${out}"
|
||||
}
|
||||
|
||||
__headscale_process_completion_results() {
|
||||
local shellCompDirectiveError=1
|
||||
local shellCompDirectiveNoSpace=2
|
||||
local shellCompDirectiveNoFileComp=4
|
||||
local shellCompDirectiveFilterFileExt=8
|
||||
local shellCompDirectiveFilterDirs=16
|
||||
local shellCompDirectiveKeepOrder=32
|
||||
|
||||
if (((directive & shellCompDirectiveError) != 0)); then
|
||||
# Error code. No completion.
|
||||
__headscale_debug "Received error from custom completion go code"
|
||||
return
|
||||
else
|
||||
if (((directive & shellCompDirectiveNoSpace) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
__headscale_debug "Activating no space"
|
||||
compopt -o nospace
|
||||
else
|
||||
__headscale_debug "No space directive not supported in this version of bash"
|
||||
fi
|
||||
fi
|
||||
if (((directive & shellCompDirectiveKeepOrder) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
# no sort isn't supported for bash less than < 4.4
|
||||
if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
|
||||
__headscale_debug "No sort directive not supported in this version of bash"
|
||||
else
|
||||
__headscale_debug "Activating keep order"
|
||||
compopt -o nosort
|
||||
fi
|
||||
else
|
||||
__headscale_debug "No sort directive not supported in this version of bash"
|
||||
fi
|
||||
fi
|
||||
if (((directive & shellCompDirectiveNoFileComp) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
__headscale_debug "Activating no file completion"
|
||||
compopt +o default
|
||||
else
|
||||
__headscale_debug "No file completion directive not supported in this version of bash"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Separate activeHelp from normal completions
|
||||
local completions=()
|
||||
local activeHelp=()
|
||||
__headscale_extract_activeHelp
|
||||
|
||||
if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
|
||||
# File extension filtering
|
||||
local fullFilter filter filteringCmd
|
||||
|
||||
# Do not use quotes around the $completions variable or else newline
|
||||
# characters will be kept.
|
||||
for filter in ${completions[*]}; do
|
||||
fullFilter+="$filter|"
|
||||
done
|
||||
|
||||
filteringCmd="_filedir $fullFilter"
|
||||
__headscale_debug "File filtering command: $filteringCmd"
|
||||
$filteringCmd
|
||||
elif (((directive & shellCompDirectiveFilterDirs) != 0)); then
|
||||
# File completion for directories only
|
||||
|
||||
local subdir
|
||||
subdir=${completions[0]}
|
||||
if [[ -n $subdir ]]; then
|
||||
__headscale_debug "Listing directories in $subdir"
|
||||
pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
|
||||
else
|
||||
__headscale_debug "Listing directories in ."
|
||||
_filedir -d
|
||||
fi
|
||||
else
|
||||
__headscale_handle_completion_types
|
||||
fi
|
||||
|
||||
__headscale_handle_special_char "$cur" :
|
||||
__headscale_handle_special_char "$cur" =
|
||||
|
||||
# Print the activeHelp statements before we finish
|
||||
if ((${#activeHelp[*]} != 0)); then
|
||||
printf "\n";
|
||||
printf "%s\n" "${activeHelp[@]}"
|
||||
printf "\n"
|
||||
|
||||
# The prompt format is only available from bash 4.4.
|
||||
# We test if it is available before using it.
|
||||
if (x=${PS1@P}) 2> /dev/null; then
|
||||
printf "%s" "${PS1@P}${COMP_LINE[@]}"
|
||||
else
|
||||
# Can't print the prompt. Just print the
|
||||
# text the user had typed, it is workable enough.
|
||||
printf "%s" "${COMP_LINE[@]}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Separate activeHelp lines from real completions.
|
||||
# Fills the $activeHelp and $completions arrays.
|
||||
__headscale_extract_activeHelp() {
|
||||
local activeHelpMarker="_activeHelp_ "
|
||||
local endIndex=${#activeHelpMarker}
|
||||
|
||||
while IFS='' read -r comp; do
|
||||
if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
|
||||
comp=${comp:endIndex}
|
||||
__headscale_debug "ActiveHelp found: $comp"
|
||||
if [[ -n $comp ]]; then
|
||||
activeHelp+=("$comp")
|
||||
fi
|
||||
else
|
||||
# Not an activeHelp line but a normal completion
|
||||
completions+=("$comp")
|
||||
fi
|
||||
done <<<"${out}"
|
||||
}
|
||||
|
||||
__headscale_handle_completion_types() {
|
||||
__headscale_debug "__headscale_handle_completion_types: COMP_TYPE is $COMP_TYPE"
|
||||
|
||||
case $COMP_TYPE in
|
||||
37|42)
|
||||
# Type: menu-complete/menu-complete-backward and insert-completions
|
||||
# If the user requested inserting one completion at a time, or all
|
||||
# completions at once on the command-line we must remove the descriptions.
|
||||
# https://github.com/spf13/cobra/issues/1508
|
||||
local tab=$'\t' comp
|
||||
while IFS='' read -r comp; do
|
||||
[[ -z $comp ]] && continue
|
||||
# Strip any description
|
||||
comp=${comp%%$tab*}
|
||||
# Only consider the completions that match
|
||||
if [[ $comp == "$cur"* ]]; then
|
||||
COMPREPLY+=("$comp")
|
||||
fi
|
||||
done < <(printf "%s\n" "${completions[@]}")
|
||||
;;
|
||||
|
||||
*)
|
||||
# Type: complete (normal completion)
|
||||
__headscale_handle_standard_completion_case
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__headscale_handle_standard_completion_case() {
|
||||
local tab=$'\t' comp
|
||||
|
||||
# Short circuit to optimize if we don't have descriptions
|
||||
if [[ "${completions[*]}" != *$tab* ]]; then
|
||||
IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
|
||||
return 0
|
||||
fi
|
||||
|
||||
local longest=0
|
||||
local compline
|
||||
# Look for the longest completion so that we can format things nicely
|
||||
while IFS='' read -r compline; do
|
||||
[[ -z $compline ]] && continue
|
||||
# Strip any description before checking the length
|
||||
comp=${compline%%$tab*}
|
||||
# Only consider the completions that match
|
||||
[[ $comp == "$cur"* ]] || continue
|
||||
COMPREPLY+=("$compline")
|
||||
if ((${#comp}>longest)); then
|
||||
longest=${#comp}
|
||||
fi
|
||||
done < <(printf "%s\n" "${completions[@]}")
|
||||
|
||||
# If there is a single completion left, remove the description text
|
||||
if ((${#COMPREPLY[*]} == 1)); then
|
||||
__headscale_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
|
||||
comp="${COMPREPLY[0]%%$tab*}"
|
||||
__headscale_debug "Removed description from single completion, which is now: ${comp}"
|
||||
COMPREPLY[0]=$comp
|
||||
else # Format the descriptions
|
||||
__headscale_format_comp_descriptions $longest
|
||||
fi
|
||||
}
|
||||
|
||||
__headscale_handle_special_char()
|
||||
{
|
||||
local comp="$1"
|
||||
local char=$2
|
||||
if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
|
||||
local word=${comp%"${comp##*${char}}"}
|
||||
local idx=${#COMPREPLY[*]}
|
||||
while ((--idx >= 0)); do
|
||||
COMPREPLY[idx]=${COMPREPLY[idx]#"$word"}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
__headscale_format_comp_descriptions()
|
||||
{
|
||||
local tab=$'\t'
|
||||
local comp desc maxdesclength
|
||||
local longest=$1
|
||||
|
||||
local i ci
|
||||
for ci in ${!COMPREPLY[*]}; do
|
||||
comp=${COMPREPLY[ci]}
|
||||
# Properly format the description string which follows a tab character if there is one
|
||||
if [[ "$comp" == *$tab* ]]; then
|
||||
__headscale_debug "Original comp: $comp"
|
||||
desc=${comp#*$tab}
|
||||
comp=${comp%%$tab*}
|
||||
|
||||
# $COLUMNS stores the current shell width.
|
||||
# Remove an extra 4 because we add 2 spaces and 2 parentheses.
|
||||
maxdesclength=$(( COLUMNS - longest - 4 ))
|
||||
|
||||
# Make sure we can fit a description of at least 8 characters
|
||||
# if we are to align the descriptions.
|
||||
if ((maxdesclength > 8)); then
|
||||
# Add the proper number of spaces to align the descriptions
|
||||
for ((i = ${#comp} ; i < longest ; i++)); do
|
||||
comp+=" "
|
||||
done
|
||||
else
|
||||
# Don't pad the descriptions so we can fit more text after the completion
|
||||
maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
|
||||
fi
|
||||
|
||||
# If there is enough space for any description text,
|
||||
# truncate the descriptions that are too long for the shell width
|
||||
if ((maxdesclength > 0)); then
|
||||
if ((${#desc} > maxdesclength)); then
|
||||
desc=${desc:0:$(( maxdesclength - 1 ))}
|
||||
desc+="…"
|
||||
fi
|
||||
comp+=" ($desc)"
|
||||
fi
|
||||
COMPREPLY[ci]=$comp
|
||||
__headscale_debug "Final comp: $comp"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__start_headscale()
|
||||
{
|
||||
local cur prev words cword split
|
||||
|
||||
COMPREPLY=()
|
||||
|
||||
# Call _init_completion from the bash-completion package
|
||||
# to prepare the arguments properly
|
||||
if declare -F _init_completion >/dev/null 2>&1; then
|
||||
_init_completion -n =: || return
|
||||
else
|
||||
__headscale_init_completion -n =: || return
|
||||
fi
|
||||
|
||||
__headscale_debug
|
||||
__headscale_debug "========= starting completion logic =========="
|
||||
__headscale_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword"
|
||||
|
||||
# The user could have moved the cursor backwards on the command-line.
|
||||
# We need to trigger completion from the $cword location, so we need
|
||||
# to truncate the command-line ($words) up to the $cword location.
|
||||
words=("${words[@]:0:$cword+1}")
|
||||
__headscale_debug "Truncated words[*]: ${words[*]},"
|
||||
|
||||
local out directive
|
||||
__headscale_get_completion_results
|
||||
__headscale_process_completion_results
|
||||
}
|
||||
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
complete -o default -F __start_headscale headscale
|
||||
else
|
||||
complete -o default -o nospace -F __start_headscale headscale
|
||||
fi
|
||||
|
||||
# ex: ts=4 sw=4 et filetype=sh
|
|
@ -1,70 +0,0 @@
|
|||
{
|
||||
config,
|
||||
mainUser,
|
||||
...
|
||||
}: let
|
||||
inherit (config.networking) hostName;
|
||||
|
||||
clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip;
|
||||
in {
|
||||
users.users.${mainUser}.extraGroups = ["headscale"];
|
||||
|
||||
services.headscale = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
server_url = "https://headscale.nelim.org";
|
||||
listen_addr = "${clusterIP}:8085";
|
||||
prefixes = {
|
||||
v4 = "100.64.0.0/10";
|
||||
v6 = "fd7a:115c:a1e0::/48";
|
||||
};
|
||||
metrics_listen_addr = "127.0.0.1:9090";
|
||||
grpc_listen_addr = "0.0.0.0:50443";
|
||||
grpc_allow_insecure = false;
|
||||
disable_check_updates = true;
|
||||
ephemeral_node_inactivity_timeout = "30m";
|
||||
unix_socket = "/run/headscale/headscale.sock";
|
||||
unix_socket_permission = "0770";
|
||||
|
||||
database = {
|
||||
type = "sqlite";
|
||||
sqlite.path = "/var/lib/headscale/db.sqlite";
|
||||
};
|
||||
|
||||
private_key_path = "/var/lib/headscale/private.key";
|
||||
noise.private_key_path = "/var/lib/headscale/noise_private.key";
|
||||
|
||||
dns = let
|
||||
caddyIp =
|
||||
if hostName == "thingone"
|
||||
then "100.64.0.8"
|
||||
else "100.64.0.9";
|
||||
in {
|
||||
magic_dns = false;
|
||||
override_local_dns = true;
|
||||
nameservers.global = [caddyIp];
|
||||
};
|
||||
|
||||
log = {
|
||||
format = "text";
|
||||
level = "info";
|
||||
};
|
||||
|
||||
derp = {
|
||||
auto_update_enable = true;
|
||||
update_frequency = "24h";
|
||||
|
||||
server = {
|
||||
enabled = true;
|
||||
stun_listen_addr = "${clusterIP}:3479";
|
||||
private_key_path = "/var/lib/headscale/derp_server_private.key";
|
||||
|
||||
region_id = 995;
|
||||
region_code = "mon";
|
||||
region_name = "montreal";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
{pkgs, ...}: {
|
||||
# NFS client setup
|
||||
services.rpcbind.enable = true;
|
||||
boot.supportedFilesystems = ["nfs"];
|
||||
environment.systemPackages = builtins.attrValues {
|
||||
inherit (pkgs) nfs-utils;
|
||||
};
|
||||
|
||||
systemd.mounts = let
|
||||
host = "10.0.0.249";
|
||||
in [
|
||||
{
|
||||
type = "nfs";
|
||||
mountConfig = {
|
||||
Options = "noatime";
|
||||
};
|
||||
what = "${host}:/caddy";
|
||||
where = "/var/lib/caddy";
|
||||
requiredBy = ["caddy.service"];
|
||||
}
|
||||
|
||||
{
|
||||
type = "nfs";
|
||||
mountConfig = {
|
||||
Options = "noatime";
|
||||
};
|
||||
what = "${host}:/headscale";
|
||||
where = "/var/lib/headscale";
|
||||
requiredBy = ["headscale.service"];
|
||||
}
|
||||
];
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
config,
|
||||
pcsd,
|
||||
...
|
||||
}: let
|
||||
inherit (config.sops) secrets;
|
||||
in {
|
||||
imports = [pcsd.nixosModules.default];
|
||||
|
||||
services.pcsd = {
|
||||
enable = true;
|
||||
enableBinaryCache = true;
|
||||
enableWebUI = true;
|
||||
|
||||
clusterName = "thingies";
|
||||
|
||||
corosyncKeyFile = secrets.corosync.path;
|
||||
clusterUserPasswordFile = secrets.pcs-pass.path;
|
||||
|
||||
virtualIps = {
|
||||
"caddy-vip" = {
|
||||
ip = "10.0.0.130";
|
||||
interface = "eno1";
|
||||
group = "caddy-grp";
|
||||
};
|
||||
};
|
||||
|
||||
systemdResources = {
|
||||
"unbound" = {
|
||||
enable = true;
|
||||
group = "caddy-grp";
|
||||
startAfter = ["caddy-vip"];
|
||||
};
|
||||
|
||||
"blocky" = {
|
||||
enable = true;
|
||||
group = "caddy-grp";
|
||||
startAfter = ["unbound"];
|
||||
};
|
||||
|
||||
"headscale" = {
|
||||
enable = true;
|
||||
group = "caddy-grp";
|
||||
startAfter = ["blocky"];
|
||||
};
|
||||
|
||||
"caddy" = {
|
||||
enable = true;
|
||||
group = "caddy-grp";
|
||||
startAfter = ["headscale"];
|
||||
};
|
||||
};
|
||||
|
||||
nodes = [
|
||||
{
|
||||
name = "thingone";
|
||||
nodeid = 1;
|
||||
ring_addrs = ["10.0.0.244"];
|
||||
}
|
||||
{
|
||||
name = "thingtwo";
|
||||
nodeid = 2;
|
||||
ring_addrs = ["10.0.0.159"];
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
mainUser,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) foldl isList mapAttrsToList mergeAttrsWithFunc remove unique;
|
||||
mergeAttrsList = list:
|
||||
foldl (mergeAttrsWithFunc (a: b:
|
||||
if isList a && isList b
|
||||
then unique (a ++ b)
|
||||
else b)) {}
|
||||
list;
|
||||
|
||||
inherit (config.networking) hostName;
|
||||
|
||||
serviviIP = "100.64.0.7";
|
||||
caddyIp =
|
||||
if hostName == "thingone"
|
||||
then "100.64.0.8"
|
||||
else "100.64.0.9";
|
||||
in {
|
||||
# https://github.com/MatthewVance/unbound-docker-rpi/issues/4#issuecomment-1001879602
|
||||
boot.kernel.sysctl."net.core.rmem_max" = 1048576;
|
||||
|
||||
users.users.${mainUser}.extraGroups = ["unbound"];
|
||||
|
||||
services.unbound = {
|
||||
enable = true;
|
||||
enableRootTrustAnchor = true;
|
||||
resolveLocalQueries = false;
|
||||
|
||||
settings = {
|
||||
server = let
|
||||
mkLocalEntry = domain: ip: {
|
||||
local-zone = ["${domain} redirect"];
|
||||
local-data = ["\"${domain} IN A ${ip}\""];
|
||||
};
|
||||
|
||||
mkMinecraftEntry = domain: port: {
|
||||
local-zone = ["${domain} transparent"];
|
||||
local-data = [
|
||||
"\"${domain} IN A ${serviviIP}\""
|
||||
"\"_minecraft._tcp.${domain}. 180 IN SRV 0 0 ${toString port} ${domain}.\""
|
||||
];
|
||||
};
|
||||
|
||||
forceResolveEntry = domain: {
|
||||
local-zone = ["${domain} always_transparent"];
|
||||
};
|
||||
|
||||
publicApps = remove "nelim.org" (mapAttrsToList (n: v: v.hostName) config.services.caddy.virtualHosts);
|
||||
in
|
||||
mergeAttrsList (
|
||||
[(mkLocalEntry "cache-apt.nelim.org" "100.64.0.10")]
|
||||
++ (map forceResolveEntry publicApps)
|
||||
++ [
|
||||
(mkMinecraftEntry "mc.nelim.org" 25569)
|
||||
(mkMinecraftEntry "mc2.nelim.org" 25560)
|
||||
(mkMinecraftEntry "cv.nelim.org" 25566)
|
||||
|
||||
(mkLocalEntry "nelim.org" caddyIp)
|
||||
|
||||
{
|
||||
interface = ["127.0.0.1"];
|
||||
port = 5335;
|
||||
|
||||
do-ip4 = true;
|
||||
do-ip6 = false;
|
||||
prefer-ip6 = false;
|
||||
do-udp = true;
|
||||
do-tcp = true;
|
||||
|
||||
# Performance
|
||||
prefetch = true;
|
||||
num-threads = 1;
|
||||
|
||||
private-address = [
|
||||
"172.16.0.0/12"
|
||||
"10.0.0.0/8"
|
||||
"100.64.0.0/8"
|
||||
"fd00::/8"
|
||||
"fe80::/10"
|
||||
];
|
||||
|
||||
# Default stuff
|
||||
harden-glue = true;
|
||||
harden-dnssec-stripped = true;
|
||||
use-caps-for-id = false;
|
||||
edns-buffer-size = 1232;
|
||||
so-rcvbuf = "1m";
|
||||
}
|
||||
]
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
{
|
||||
mainUser,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
# ------------------------------------------------
|
||||
# Imports
|
||||
# ------------------------------------------------
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
|
||||
./modules
|
||||
|
||||
self.nixosModules.base
|
||||
self.nixosModules.docker
|
||||
self.nixosModules.kmscon
|
||||
self.nixosModules.server
|
||||
];
|
||||
|
||||
# State Version: DO NOT CHANGE
|
||||
system.stateVersion = "24.11";
|
||||
|
||||
# ------------------------------------------------
|
||||
# User Settings
|
||||
# ------------------------------------------------
|
||||
users.users.${mainUser} = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"adm"
|
||||
];
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = "homie";
|
||||
resolvconf.enable = true;
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
time.timeZone = "America/Montreal";
|
||||
|
||||
# ------------------------------------------------
|
||||
# `Self` Modules configuration
|
||||
# ------------------------------------------------
|
||||
roles.base = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
user = mainUser;
|
||||
tailscale.enable = true;
|
||||
sshd.enable = true;
|
||||
};
|
||||
|
||||
khepri.enable = true;
|
||||
services.kmscon.enable = true;
|
||||
|
||||
home-manager.users.${mainUser} = {
|
||||
imports = [
|
||||
self.homeManagerModules.neovim
|
||||
self.homeManagerModules.shell
|
||||
];
|
||||
|
||||
programs = {
|
||||
bash = {
|
||||
enable = true;
|
||||
promptMainColor = "yellow";
|
||||
};
|
||||
|
||||
neovim = {
|
||||
enable = true;
|
||||
user = mainUser;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
config,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
|
||||
|
||||
boot = {
|
||||
loader = {
|
||||
efi.canTouchEfiVariables = true;
|
||||
timeout = 2;
|
||||
|
||||
systemd-boot = {
|
||||
enable = true;
|
||||
consoleMode = "max";
|
||||
configurationLimit = 30;
|
||||
};
|
||||
};
|
||||
|
||||
initrd.availableKernelModules = [
|
||||
"xhci_pci"
|
||||
"ahci"
|
||||
"nvme"
|
||||
"usbhid"
|
||||
"usb_storage"
|
||||
"sd_mod"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
};
|
||||
|
||||
# sudo btrfs subvolume create /@swap
|
||||
"/swap" = {
|
||||
device = "/dev/disk/by-label/NIXROOT";
|
||||
fsType = "btrfs";
|
||||
options = ["subvol=@swap"];
|
||||
};
|
||||
|
||||
"/boot" = {
|
||||
device = "/dev/disk/by-label/NIXBOOT";
|
||||
fsType = "vfat";
|
||||
options = ["fmask=0022" "dmask=0022"];
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/swap/swapfile";
|
||||
size = 16 * 1024;
|
||||
}
|
||||
];
|
||||
|
||||
zramSwap.enable = true;
|
||||
|
||||
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
# Unfortunately I had some hardware issues but this does work
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) mkForce getExe;
|
||||
|
||||
connectControllers = getExe (pkgs.writeShellApplication {
|
||||
name = "connectControllers";
|
||||
runtimeInputs = with pkgs; [gnugrep usbutils];
|
||||
text = ''
|
||||
set +o errexit
|
||||
|
||||
for dev in /sys/bus/usb/devices/*; do
|
||||
vendor="$(cat "$dev/idVendor" 2>/dev/null)"
|
||||
prod="$(cat "$dev/idProduct" 2>/dev/null)"
|
||||
|
||||
if [[ "$vendor" != "" && "$prod" != "" ]]; then
|
||||
if [[ "$(lsusb -d "$vendor:$prod" | grep "Microsoft Corp. Xbox Controller")" != "" ]]; then
|
||||
echo 0 > "$dev/authorized"
|
||||
echo 1 > "$dev/authorized"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
'';
|
||||
});
|
||||
|
||||
hyprConf = pkgs.writeText "greetd-hypr-config" ''
|
||||
cursor {
|
||||
inactive_timeout = 1
|
||||
}
|
||||
|
||||
misc {
|
||||
disable_hyprland_logo = true
|
||||
disable_splash_rendering = true
|
||||
}
|
||||
|
||||
decoration {
|
||||
blur {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
animations {
|
||||
enabled = false
|
||||
first_launch_animation = false
|
||||
}
|
||||
|
||||
bind = SUPER, Q, exec, kitty
|
||||
|
||||
windowrule = fullscreen, ^(.*)$
|
||||
exec-once = waydroid show-full-ui
|
||||
exec-once = sleep 10; sudo ${connectControllers}
|
||||
'';
|
||||
|
||||
user = "matt";
|
||||
command = "Hyprland --config ${hyprConf}";
|
||||
|
||||
session = {inherit command user;};
|
||||
in {
|
||||
# Make it so we don't need root to connect controllers
|
||||
security.sudo.extraRules = [
|
||||
{
|
||||
users = [user];
|
||||
groups = [user];
|
||||
commands = [
|
||||
{
|
||||
command = connectControllers;
|
||||
options = ["SETENV" "NOPASSWD"];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# TODO: make the following declarative and also make the image declarative
|
||||
# Add this to /var/lib/waydroid/waydroid.cfg for controller support
|
||||
# persist.waydroid.udev = true
|
||||
# persist.waydroid.uevent = true
|
||||
virtualisation.waydroid.enable = true;
|
||||
|
||||
users.users."greeter" = {
|
||||
home = "/var/lib/greeter";
|
||||
};
|
||||
|
||||
programs.hyprland.enable = true;
|
||||
|
||||
services = {
|
||||
greetd = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
default_session = session;
|
||||
initial_session = session;
|
||||
};
|
||||
};
|
||||
|
||||
pipewire.enable = mkForce false;
|
||||
};
|
||||
|
||||
environment.systemPackages = [pkgs.kitty];
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (builtins) attrValues;
|
||||
inherit (config.sops) secrets;
|
||||
|
||||
nixFastBuild = pkgs.writeShellApplication {
|
||||
name = "nixFastBuild";
|
||||
|
||||
runtimeInputs = attrValues {
|
||||
inherit
|
||||
(pkgs)
|
||||
gnugrep
|
||||
nix-fast-build
|
||||
nix-output-monitor
|
||||
;
|
||||
};
|
||||
|
||||
text = ''
|
||||
cd "$FLAKE/results" || return
|
||||
|
||||
nix-fast-build -f ..#nixFastChecks.aptDevices "$@"
|
||||
'';
|
||||
};
|
||||
in {
|
||||
services.nix-serve = {
|
||||
enable = true;
|
||||
secretKeyFile = secrets.apt-binary-cache-key.path;
|
||||
};
|
||||
|
||||
environment.systemPackages = [pkgs.nix-fast-build nixFastBuild];
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{...}: {
|
||||
imports = [
|
||||
./binary-cache.nix
|
||||
./home-assistant
|
||||
./music
|
||||
];
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
self,
|
||||
wakewords-src,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
self.nixosModules.esphome-plus
|
||||
self.nixosModules.wyoming-plus
|
||||
];
|
||||
|
||||
services = {
|
||||
home-assistant = {
|
||||
package = pkgs.home-assistant.override {
|
||||
packageOverrides = final: prev: {
|
||||
# HassTimer has way too many collisions with my custom timer sentences
|
||||
home-assistant-intents = prev.home-assistant-intents.overrideAttrs (o: {
|
||||
nativeBuildInputs = o.nativeBuildInputs ++ [pkgs.findutils];
|
||||
postPatch = ''
|
||||
find ./. -name "*Timer*" -delete
|
||||
find ./. -name "*Start*" -delete
|
||||
'';
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
customComponents = builtins.attrValues {
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.hass-components)
|
||||
extended-ollama-conversation # url is without subdirectory
|
||||
ha-fallback-conversation
|
||||
tuya-local
|
||||
;
|
||||
};
|
||||
|
||||
extraComponents = [
|
||||
"esphome"
|
||||
"ollama"
|
||||
"wyoming"
|
||||
"scrape"
|
||||
];
|
||||
|
||||
config = {
|
||||
assist_pipeline = {};
|
||||
conversation = {};
|
||||
media_source = {};
|
||||
};
|
||||
};
|
||||
|
||||
wyoming = {
|
||||
piper.servers."en" = {
|
||||
enable = true;
|
||||
uri = "tcp://127.0.0.1:10200";
|
||||
|
||||
# see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py
|
||||
voice = "en-us-ryan-low"; # using `hfc male (medium)` in GUI
|
||||
speaker = 0;
|
||||
};
|
||||
|
||||
openwakeword = {
|
||||
enable = true;
|
||||
uri = "tcp://127.0.0.1:10400";
|
||||
|
||||
threshold = 0.55;
|
||||
vadThreshold = 0.50;
|
||||
|
||||
customModelsDirectories = ["${wakewords-src}/en/yo_homie"];
|
||||
preloadModels = ["yo_homie"];
|
||||
|
||||
extraArgs = ["--debug"];
|
||||
};
|
||||
};
|
||||
|
||||
esphome = {
|
||||
enable = true;
|
||||
address = "100.64.0.10";
|
||||
port = 6052;
|
||||
};
|
||||
};
|
||||
|
||||
# In case tailscale is down
|
||||
boot.kernel.sysctl."net.ipv4.ip_nonlocal_bind" = 1;
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) getExe;
|
||||
|
||||
turnOnUE = pkgs.writeShellApplication {
|
||||
name = "turnOnUE";
|
||||
|
||||
runtimeInputs = [config.hardware.bluetooth.package];
|
||||
|
||||
text = ''
|
||||
cmd=0x0003
|
||||
bt_device_addr=88:C6:26:93:4B:77
|
||||
|
||||
# This is the MAC address on the first line of `bluetootctl show`
|
||||
# without the `:` and with `01` added at the end
|
||||
bt_controller_addr=E848B8C8200001
|
||||
|
||||
exec gatttool -b $bt_device_addr --char-write-req --handle=$cmd --value=$bt_controller_addr
|
||||
'';
|
||||
};
|
||||
in {
|
||||
environment.systemPackages = [turnOnUE];
|
||||
|
||||
services.home-assistant = {
|
||||
extraComponents = [
|
||||
"mpd"
|
||||
|
||||
# BT components
|
||||
"ibeacon"
|
||||
"led_ble"
|
||||
"kegtron"
|
||||
"xiaomi_ble"
|
||||
];
|
||||
|
||||
# Turn On the speaker automatically when openwakeword is used
|
||||
config = {
|
||||
shell_command.turn_on_ue = getExe turnOnUE;
|
||||
script.turn_on_ue = {
|
||||
alias = "Music - TurnOnUE";
|
||||
description = "Script for turning on the UE Boom 2 speaker.";
|
||||
icon = "mdi:music";
|
||||
mode = "single";
|
||||
|
||||
sequence = [
|
||||
{
|
||||
alias = "Run shell command";
|
||||
service = "shell_command.turn_on_ue";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
automation = [
|
||||
{
|
||||
alias = "Turn On UE";
|
||||
mode = "single";
|
||||
trigger = [
|
||||
{
|
||||
platform = "state";
|
||||
entity_id = "wake_word.openwakeword";
|
||||
}
|
||||
];
|
||||
condition = [];
|
||||
action = [
|
||||
{
|
||||
action = "shell_command.turn_on_ue";
|
||||
metadata = {};
|
||||
data = {};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
{
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
./assist.nix
|
||||
./bluetooth.nix
|
||||
./firmware.nix
|
||||
./frontend.nix
|
||||
|
||||
./netdaemon
|
||||
./spotify
|
||||
./timer
|
||||
|
||||
self.nixosModules.ha-plus
|
||||
];
|
||||
|
||||
services.home-assistant = {
|
||||
enable = true;
|
||||
|
||||
extraComponents = [
|
||||
"androidtv"
|
||||
"androidtv_remote"
|
||||
"caldav"
|
||||
"cast"
|
||||
"holiday"
|
||||
"isal"
|
||||
"met"
|
||||
"switchbot"
|
||||
"upnp"
|
||||
"yamaha_musiccast"
|
||||
];
|
||||
|
||||
customComponents = builtins.attrValues {
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.hass-components)
|
||||
yamaha-soundbar
|
||||
;
|
||||
};
|
||||
|
||||
config = {
|
||||
homeassistant = {
|
||||
name = "Home";
|
||||
unit_system = "metric";
|
||||
currency = "CAD";
|
||||
country = "CA";
|
||||
time_zone = "America/Montreal";
|
||||
external_url = "https://homie.nelim.org";
|
||||
};
|
||||
|
||||
media_player = [
|
||||
{
|
||||
platform = "yamaha_soundbar";
|
||||
host = "192.168.0.96";
|
||||
name = "Living Room Speaker";
|
||||
sources = {
|
||||
HDMI = "TV";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# Proxy settings
|
||||
http = {
|
||||
server_host = "0.0.0.0";
|
||||
trusted_proxies = ["100.64.0.8" "100.64.0.9"];
|
||||
use_x_forwarded_for = true;
|
||||
};
|
||||
|
||||
# `default_config` enables too much stuff. this is what I want from it
|
||||
config = {};
|
||||
dhcp = {};
|
||||
history = {};
|
||||
image_upload = {};
|
||||
logbook = {};
|
||||
mobile_app = {};
|
||||
my = {};
|
||||
sun = {};
|
||||
zeroconf = {};
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellApplication {
|
||||
name = "yaml2nix";
|
||||
runtimeInputs = with pkgs; [yj];
|
||||
text = ''
|
||||
input="$(yj < "$1")"
|
||||
output="''${2:-""}"
|
||||
|
||||
nixCode="$(nix eval --expr "builtins.fromJSON '''$input'''" --impure | alejandra -q | sed 's/ = null;/ = {};/g')"
|
||||
|
||||
if [[ "$output" != "" ]]; then
|
||||
echo "$nixCode" > "$output"
|
||||
else
|
||||
echo "$nixCode"
|
||||
fi
|
||||
'';
|
||||
})
|
||||
|
||||
(pkgs.writeShellApplication {
|
||||
name = "nix2yaml";
|
||||
runtimeInputs = with pkgs; [remarshal];
|
||||
text = ''
|
||||
input="$1"
|
||||
output="''${2:-""}"
|
||||
|
||||
yamlCode="$(nix eval --json --file "$input" | remarshal --if json --of yaml)"
|
||||
|
||||
if [[ "$output" != "" ]]; then
|
||||
echo "$yamlCode" > "$output"
|
||||
else
|
||||
echo "$yamlCode"
|
||||
fi
|
||||
'';
|
||||
})
|
||||
];
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
# I use nix2yaml from ../default.nix to convert this to YAML and place it in the functions of extended_ollama_conversation
|
||||
[
|
||||
{
|
||||
spec = {
|
||||
name = "get_attributes";
|
||||
description = "Get attributes of any home assistant entity";
|
||||
parameters = {
|
||||
type = "object";
|
||||
|
||||
properties = {
|
||||
entity_id = {
|
||||
type = "string";
|
||||
description = "entity_id";
|
||||
};
|
||||
};
|
||||
|
||||
required = ["entity_id"];
|
||||
};
|
||||
};
|
||||
|
||||
function = {
|
||||
type = "template";
|
||||
value_template = "{{ states[entity_id] }}";
|
||||
};
|
||||
}
|
||||
]
|
|
@ -1,51 +0,0 @@
|
|||
{%- set customize_glob_exposed_attributes = {
|
||||
".*": {
|
||||
"friendly_name": true,
|
||||
"temperature": true,
|
||||
"current_temperature": true,
|
||||
"temperature_unit": true,
|
||||
"brightness": true,
|
||||
"humidity": true,
|
||||
"unit_of_measurement": true,
|
||||
"device_class": true,
|
||||
"current_position": true,
|
||||
"percentage": true,
|
||||
"volume_level": true,
|
||||
"media_title": true,
|
||||
"media_artist": true,
|
||||
"media_album_name": true,
|
||||
},
|
||||
} %}
|
||||
|
||||
{%- macro get_exposed_attributes(entity_id) -%}
|
||||
{%- set ns = namespace(exposed_attributes = {}, result = {}) %}
|
||||
{%- for pattern, attributes in customize_glob_exposed_attributes.items() -%}
|
||||
{%- if entity_id | regex_match(pattern) -%}
|
||||
{%- set ns.exposed_attributes = dict(ns.exposed_attributes, **attributes) -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- for attribute_key, should_include in ns.exposed_attributes.items() -%}
|
||||
{%- if should_include and state_attr(entity_id, attribute_key) != None -%}
|
||||
{%- set temp = {attribute_key: state_attr(entity_id, attribute_key)} if should_include is boolean else {attribute_key: should_include} -%}
|
||||
{%- set ns.result = dict(ns.result, **temp) -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- set result = ns.result | to_json if ns.result!={} else None -%}
|
||||
{{"'" + result + "'" if result != None else ''}}
|
||||
{%- endmacro -%}
|
||||
|
||||
I want you to act as a personal assistant who is aware of my smart home.
|
||||
You will truthfully answer in one sentence in everyday language.
|
||||
|
||||
Current Time: {{now()}}
|
||||
|
||||
Available Devices:
|
||||
```csv
|
||||
entity_id,name,state,aliases,attributes
|
||||
{% for entity in exposed_entities -%}
|
||||
{{ entity.entity_id }},{{ entity.name }},{{ entity.state }},{{entity.aliases | join('/')}},{{get_exposed_attributes(entity.entity_id)}}
|
||||
{% endfor -%}
|
||||
```
|
||||
|
||||
The current state of devices is provided in available devices.
|
||||
Do not restate what user says.
|
|
@ -1,383 +0,0 @@
|
|||
{config, ...}: {
|
||||
services.esphome = {
|
||||
secretsFile = config.sops.secrets.esphome.path;
|
||||
|
||||
firmwareConfigs = {
|
||||
# -------------------------------------------------------------
|
||||
"AtomEcho" = {
|
||||
# Device specific settings
|
||||
api.encryption.key = "!secret api_key";
|
||||
|
||||
ota = [
|
||||
{
|
||||
platform = "esphome";
|
||||
password = "!secret ota_pass";
|
||||
}
|
||||
];
|
||||
|
||||
wifi = {
|
||||
ssid = "!secret wifi_ssid";
|
||||
password = "!secret wifi_password";
|
||||
|
||||
manual_ip = {
|
||||
static_ip = "192.168.0.92";
|
||||
gateway = "192.168.0.1";
|
||||
subnet = "255.255.255.0";
|
||||
};
|
||||
|
||||
ap = {
|
||||
ssid = "Esp1 Fallback Hotspot";
|
||||
password = "!secret ap_fallback";
|
||||
};
|
||||
};
|
||||
|
||||
# Hardware Declaration
|
||||
esphome = {
|
||||
friendly_name = "M5Stack Atom Echo";
|
||||
min_version = "2024.9.0";
|
||||
name = "m5stack-atom-echo";
|
||||
name_add_mac_suffix = true;
|
||||
};
|
||||
|
||||
esp32 = {
|
||||
board = "m5stack-atom";
|
||||
framework.type = "esp-idf";
|
||||
};
|
||||
|
||||
esp_adf = {};
|
||||
|
||||
button = [
|
||||
{
|
||||
id = "button_safe_mode";
|
||||
name = "Safe Mode Boot";
|
||||
platform = "safe_mode";
|
||||
}
|
||||
{
|
||||
id = "factory_reset_btn";
|
||||
name = "Factory reset";
|
||||
platform = "factory_reset";
|
||||
}
|
||||
];
|
||||
|
||||
microphone = [
|
||||
{
|
||||
adc_type = "external";
|
||||
i2s_din_pin = "GPIO23";
|
||||
id = "echo_microphone";
|
||||
pdm = true;
|
||||
platform = "i2s_audio";
|
||||
}
|
||||
];
|
||||
|
||||
speaker = [
|
||||
{
|
||||
dac_type = "external";
|
||||
i2s_dout_pin = "GPIO21"; # "GPIO22"; turn off speaker
|
||||
id = "echo_speaker";
|
||||
channel = "mono";
|
||||
platform = "i2s_audio";
|
||||
}
|
||||
];
|
||||
|
||||
i2s_audio = [
|
||||
{
|
||||
id = "i2s_audio_bus";
|
||||
i2s_bclk_pin = "GPIO19";
|
||||
i2s_lrclk_pin = "GPIO33";
|
||||
}
|
||||
];
|
||||
|
||||
light = [
|
||||
{
|
||||
id = "led";
|
||||
name = "None";
|
||||
entity_category = "config";
|
||||
|
||||
chipset = "SK6812";
|
||||
pin = "GPIO27";
|
||||
platform = "esp32_rmt_led_strip";
|
||||
|
||||
default_transition_length = "0s";
|
||||
disabled_by_default = false;
|
||||
num_leds = 1;
|
||||
rgb_order = "grb";
|
||||
rmt_channel = 0;
|
||||
|
||||
effects = [
|
||||
{
|
||||
pulse = {
|
||||
name = "Slow Pulse";
|
||||
|
||||
max_brightness = "100%";
|
||||
min_brightness = "50%";
|
||||
transition_length = "250ms";
|
||||
update_interval = "250ms";
|
||||
};
|
||||
}
|
||||
{
|
||||
pulse = {
|
||||
name = "Fast Pulse";
|
||||
|
||||
max_brightness = "100%";
|
||||
min_brightness = "50%";
|
||||
transition_length = "100ms";
|
||||
update_interval = "100ms";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Home-assistant buttons
|
||||
switch = [
|
||||
{
|
||||
id = "use_wake_word";
|
||||
name = "Use wake word";
|
||||
entity_category = "config";
|
||||
|
||||
optimistic = true;
|
||||
platform = "template";
|
||||
restore_mode = "RESTORE_DEFAULT_ON";
|
||||
|
||||
on_turn_on = [
|
||||
{lambda = "id(va).set_use_wake_word(true);";}
|
||||
{
|
||||
"if" = {
|
||||
condition.not = ["voice_assistant.is_running"];
|
||||
"then" = ["voice_assistant.start_continuous"];
|
||||
};
|
||||
}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
|
||||
on_turn_off = [
|
||||
"voice_assistant.stop"
|
||||
{lambda = "id(va).set_use_wake_word(false);";}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
}
|
||||
|
||||
{
|
||||
id = "use_listen_light";
|
||||
name = "Use listen light";
|
||||
entity_category = "config";
|
||||
|
||||
optimistic = true;
|
||||
platform = "template";
|
||||
restore_mode = "RESTORE_DEFAULT_ON";
|
||||
|
||||
on_turn_on = [{"script.execute" = "reset_led";}];
|
||||
on_turn_off = [{"script.execute" = "reset_led";}];
|
||||
}
|
||||
];
|
||||
|
||||
binary_sensor = [
|
||||
{
|
||||
id = "echo_button";
|
||||
name = "Button";
|
||||
entity_category = "diagnostic";
|
||||
|
||||
disabled_by_default = false;
|
||||
|
||||
platform = "gpio";
|
||||
pin = {
|
||||
inverted = true;
|
||||
number = "GPIO39";
|
||||
};
|
||||
|
||||
on_multi_click = [
|
||||
{
|
||||
timing = ["ON for at least 50ms" "OFF for at least 50ms"];
|
||||
"then" = [
|
||||
{
|
||||
"if" = {
|
||||
condition = {"switch.is_off" = "use_wake_word";};
|
||||
"then" = [
|
||||
{
|
||||
"if" = {
|
||||
condition = "voice_assistant.is_running";
|
||||
"then" = [
|
||||
{"voice_assistant.stop" = {};}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
"else" = [{"voice_assistant.start" = {};}];
|
||||
};
|
||||
}
|
||||
];
|
||||
"else" = [
|
||||
"voice_assistant.stop"
|
||||
{delay = "1s";}
|
||||
{"script.execute" = "reset_led";}
|
||||
{"script.wait" = "reset_led";}
|
||||
{"voice_assistant.start_continuous" = {};}
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
timing = ["ON for at least 10s"];
|
||||
"then" = [{"button.press" = "factory_reset_btn";}];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Misc
|
||||
logger = {};
|
||||
|
||||
external_components = [
|
||||
{
|
||||
source = "github://pr#5230";
|
||||
components = ["esp_adf"];
|
||||
refresh = "0s";
|
||||
}
|
||||
];
|
||||
|
||||
# Configs
|
||||
script = [
|
||||
{
|
||||
id = "reset_led";
|
||||
"then" = [
|
||||
{
|
||||
"if" = {
|
||||
condition = [
|
||||
{"switch.is_on" = "use_wake_word";}
|
||||
{"switch.is_on" = "use_listen_light";}
|
||||
];
|
||||
"then" = [
|
||||
{
|
||||
"light.turn_on" = {
|
||||
id = "led";
|
||||
brightness = "60%";
|
||||
effect = "none";
|
||||
|
||||
red = "100%";
|
||||
green = "89%";
|
||||
blue = "71%";
|
||||
};
|
||||
}
|
||||
];
|
||||
"else" = [{"light.turn_off" = "led";}];
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
voice_assistant = {
|
||||
id = "va";
|
||||
speaker = "echo_speaker";
|
||||
microphone = "echo_microphone";
|
||||
auto_gain = "31dBFS";
|
||||
|
||||
noise_suppression_level = 2;
|
||||
vad_threshold = 3;
|
||||
volume_multiplier = 2;
|
||||
|
||||
on_listening = [
|
||||
{
|
||||
"light.turn_on" = {
|
||||
id = "led";
|
||||
effect = "Slow Pulse";
|
||||
|
||||
green = "0%";
|
||||
red = "0%";
|
||||
blue = "100%";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
on_stt_vad_end = [
|
||||
{
|
||||
"light.turn_on" = {
|
||||
id = "led";
|
||||
effect = "Fast Pulse";
|
||||
|
||||
red = "0%";
|
||||
green = "0%";
|
||||
blue = "100%";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
on_tts_start = [
|
||||
{
|
||||
"light.turn_on" = {
|
||||
id = "led";
|
||||
brightness = "100%";
|
||||
effect = "none";
|
||||
|
||||
red = "0%";
|
||||
green = "0%";
|
||||
blue = "100%";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# Play audio from bluetooth speaker
|
||||
on_tts_end = [
|
||||
{
|
||||
"homeassistant.service" = {
|
||||
service = "media_player.play_media";
|
||||
data = {
|
||||
entity_id = "media_player.music_player_daemon";
|
||||
media_content_id = "!lambda \"return x;\"";
|
||||
media_content_type = "music";
|
||||
announce = "\"true\"";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
on_end = [
|
||||
{delay = "100ms";}
|
||||
{wait_until.not."speaker.is_playing" = {};}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
|
||||
on_error = [
|
||||
{
|
||||
"light.turn_on" = {
|
||||
id = "led";
|
||||
brightness = "100%";
|
||||
effect = "none";
|
||||
|
||||
red = "100%";
|
||||
green = "0%";
|
||||
blue = "0%";
|
||||
};
|
||||
}
|
||||
{delay = "1s";}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
|
||||
on_client_connected = [
|
||||
{
|
||||
"if" = {
|
||||
condition = {"switch.is_on" = "use_wake_word";};
|
||||
"then" = [
|
||||
{"voice_assistant.start_continuous" = {};}
|
||||
{"script.execute" = "reset_led";}
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
on_client_disconnected = [
|
||||
{
|
||||
"if" = {
|
||||
condition = {"switch.is_on" = "use_wake_word";};
|
||||
"then" = [
|
||||
{"voice_assistant.stop" = {};}
|
||||
{"light.turn_off" = "led";}
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
# -------------------------------------------------------------
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
{
|
||||
caule-themes-src,
|
||||
dracul-ha-src,
|
||||
material-rounded-theme-src,
|
||||
lib,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) singleton;
|
||||
inherit (pkgs.writers) writeYAML;
|
||||
in {
|
||||
services.home-assistant = {
|
||||
configFiles = {
|
||||
"themes/caule.yaml".source = "${caule-themes-src}/themes/caule-themes-pack-1.yaml";
|
||||
"themes/dracul-ha.yaml".source = "${dracul-ha-src}/themes/dracul-ha.yaml";
|
||||
"themes/material_rounded.yaml".source = "${material-rounded-theme-src}/themes/material_rounded.yaml";
|
||||
|
||||
"www/sidebar-config.yaml".source = writeYAML "sidebar" {
|
||||
id = "my-sidebar";
|
||||
|
||||
order = [
|
||||
# Top
|
||||
{
|
||||
item = "overview";
|
||||
order = 1;
|
||||
}
|
||||
{
|
||||
match = "href";
|
||||
item = "calendar";
|
||||
order = 2;
|
||||
}
|
||||
{
|
||||
match = "href";
|
||||
item = "todo";
|
||||
order = 3;
|
||||
}
|
||||
|
||||
# Bottom
|
||||
{
|
||||
bottom = true;
|
||||
item = "esphome";
|
||||
order = 5;
|
||||
}
|
||||
{
|
||||
bottom = true;
|
||||
item = "logbook";
|
||||
order = 7;
|
||||
}
|
||||
{
|
||||
bottom = true;
|
||||
icon = "mdi:tools";
|
||||
item = "developer tools";
|
||||
name = "Developer tools";
|
||||
order = 9;
|
||||
}
|
||||
{
|
||||
bottom = true;
|
||||
item = "settings";
|
||||
order = 11;
|
||||
}
|
||||
|
||||
# Hidden
|
||||
{
|
||||
hide = true;
|
||||
item = "map";
|
||||
}
|
||||
{
|
||||
hide = true;
|
||||
item = "energy";
|
||||
}
|
||||
{
|
||||
hide = true;
|
||||
item = "history";
|
||||
}
|
||||
{
|
||||
hide = true;
|
||||
item = "media";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
customComponents = builtins.attrValues {
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.hass-components)
|
||||
material-symbols
|
||||
;
|
||||
};
|
||||
|
||||
customLovelaceModules = builtins.attrValues {
|
||||
inherit
|
||||
(pkgs.home-assistant-custom-lovelace-modules)
|
||||
card-mod
|
||||
universal-remote-card
|
||||
;
|
||||
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.lovelace-components)
|
||||
custom-sidebar
|
||||
;
|
||||
};
|
||||
|
||||
config.frontend = {
|
||||
themes = "!include_dir_merge_named themes";
|
||||
extra_module_url = map (p: "/local/nixos-lovelace-modules/${p}.js") [
|
||||
"card-mod"
|
||||
"custom-sidebar-yaml"
|
||||
];
|
||||
};
|
||||
|
||||
config.template = [
|
||||
{
|
||||
sensor = singleton {
|
||||
name = "Material Rounded Base Color Matt";
|
||||
unique_id = "material_rounded_base_color_matt";
|
||||
state = ''{{ states("sensor.pixel_8_accent_color") }}'';
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
lovelaceConfig = {
|
||||
title = "Our House";
|
||||
# I don't want multiple views
|
||||
views = singleton {
|
||||
path = "home";
|
||||
title = "Home";
|
||||
cards = [
|
||||
{
|
||||
type = "entities";
|
||||
entities = [
|
||||
"switch.smartplug1"
|
||||
"switch.smartplug3"
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "entities";
|
||||
entities = [
|
||||
"timer.assist_timer1"
|
||||
"timer.assist_timer2"
|
||||
"timer.assist_timer3"
|
||||
];
|
||||
}
|
||||
|
||||
{
|
||||
type = "custom:android-tv-card";
|
||||
|
||||
visibility = singleton {
|
||||
condition = "state";
|
||||
entity = "remote.onn_4k_streaming_box";
|
||||
state_not = ["unavailable" "unknown"];
|
||||
};
|
||||
|
||||
media_player_id = "media_player.living_room_speaker";
|
||||
keyboard_id = "remote.android_tv_192_168_0_106";
|
||||
remote_id = "remote.onn_4k_streaming_box";
|
||||
|
||||
rows = [
|
||||
"navigation_buttons"
|
||||
[null "slider" null]
|
||||
[null]
|
||||
["jellyfin" "home" "back" "keyboard"]
|
||||
[null]
|
||||
];
|
||||
|
||||
custom_actions = [
|
||||
{
|
||||
name = "center";
|
||||
type = "button";
|
||||
icon = "mdi:checkbox-blank-circle";
|
||||
|
||||
styles = ''
|
||||
:host {
|
||||
--icon-color: rgb(94, 94, 94);
|
||||
--size: 200px;
|
||||
background: rgb(31, 31, 31);
|
||||
border-radius: 200px;
|
||||
margin: -70px;
|
||||
padding: 70px;
|
||||
}
|
||||
'';
|
||||
|
||||
tap_action = {
|
||||
action = "key";
|
||||
key = "DPAD_CENTER";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
name = "up";
|
||||
type = "button";
|
||||
icon = "mdi:chevron-up";
|
||||
|
||||
styles = ''
|
||||
:host {
|
||||
--icon-color: rgb(197, 199, 197);
|
||||
z-index: 2;
|
||||
top: 25px;
|
||||
height: 90px;
|
||||
width: 300px;
|
||||
}
|
||||
'';
|
||||
|
||||
hold_action = {action = "repeat";};
|
||||
tap_action = {
|
||||
action = "key";
|
||||
key = "DPAD_UP";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
name = "down";
|
||||
type = "button";
|
||||
icon = "mdi:chevron-down";
|
||||
|
||||
styles = ''
|
||||
:host {
|
||||
--icon-color: rgb(197, 199, 197);
|
||||
z-index: 2;
|
||||
bottom: 25px;
|
||||
height: 90px;
|
||||
width: 300px;
|
||||
}
|
||||
'';
|
||||
|
||||
hold_action = {action = "repeat";};
|
||||
tap_action = {
|
||||
action = "key";
|
||||
key = "DPAD_DOWN";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
name = "left";
|
||||
type = "button";
|
||||
icon = "mdi:chevron-left";
|
||||
|
||||
styles = ''
|
||||
:host {
|
||||
--icon-color: rgb(197, 199, 197);
|
||||
z-index: 2;
|
||||
left: 30px;
|
||||
height: 170px;
|
||||
width: 90px;
|
||||
}
|
||||
'';
|
||||
|
||||
hold_action = {action = "repeat";};
|
||||
tap_action = {
|
||||
action = "key";
|
||||
key = "DPAD_LEFT";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
name = "right";
|
||||
type = "button";
|
||||
icon = "mdi:chevron-right";
|
||||
|
||||
styles = ''
|
||||
:host {
|
||||
--icon-color: rgb(197, 199, 197);
|
||||
z-index: 2;
|
||||
right: 30px;
|
||||
height: 170px;
|
||||
width: 90px;
|
||||
}
|
||||
'';
|
||||
|
||||
hold_action = {action = "repeat";};
|
||||
tap_action = {
|
||||
action = "key";
|
||||
key = "DPAD_RIGHT";
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
name = "slider";
|
||||
type = "slider";
|
||||
icon = "mdi:volume-high";
|
||||
|
||||
range = [0 1];
|
||||
step = 0.01;
|
||||
|
||||
tap_action = {
|
||||
action = "perform-action";
|
||||
perform_action = "media_player.volume_set";
|
||||
data = {
|
||||
volume_level = "{{ value | float }}";
|
||||
};
|
||||
};
|
||||
value_attribute = "volume_level";
|
||||
}
|
||||
];
|
||||
|
||||
styles = ''
|
||||
#row-1 {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#row-2 {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#row-3 {
|
||||
justify-content: center;
|
||||
}
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
config.lovelace.dashboards = {
|
||||
esphome-dash = {
|
||||
title = "ESPHome";
|
||||
icon = "mdi:car-esp";
|
||||
mode = "yaml";
|
||||
|
||||
show_in_sidebar = true;
|
||||
require_admin = true;
|
||||
|
||||
filename = writeYAML "esphome.yaml" {
|
||||
strategy = {
|
||||
type = "iframe";
|
||||
url = "https://esphome.nelim.org";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,360 +0,0 @@
|
|||
root = true
|
||||
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = true
|
||||
dotnet_sort_system_directives_first = true
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true:warning
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:suggestion
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
[*.cs]
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:suggestion
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_not_pattern = true:suggestion
|
||||
csharp_style_prefer_pattern_matching = true:silent
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true:warning
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
|
||||
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
|
||||
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.severity = none
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
|
||||
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
|
||||
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
|
||||
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interfaces.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interfaces.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.enums.applicable_kinds = enum
|
||||
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.enums.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.events.applicable_kinds = event
|
||||
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.events.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.methods.applicable_kinds = method
|
||||
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.methods.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.properties.applicable_kinds = property
|
||||
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.properties.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.public_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_fields.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_fields.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_static_fields.required_modifiers = static
|
||||
|
||||
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
|
||||
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
|
||||
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.type_parameters.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.local_variables.applicable_kinds = local
|
||||
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
|
||||
dotnet_naming_symbols.local_variables.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.local_constants.applicable_kinds = local
|
||||
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
|
||||
dotnet_naming_symbols.local_constants.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.parameters.applicable_kinds = parameter
|
||||
dotnet_naming_symbols.parameters.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.parameters.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
|
||||
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
|
||||
|
||||
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
|
||||
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.local_functions.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascalcase.required_prefix =
|
||||
dotnet_naming_style.pascalcase.required_suffix =
|
||||
dotnet_naming_style.pascalcase.word_separator =
|
||||
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.ipascalcase.required_prefix = I
|
||||
dotnet_naming_style.ipascalcase.required_suffix =
|
||||
dotnet_naming_style.ipascalcase.word_separator =
|
||||
dotnet_naming_style.ipascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.tpascalcase.required_prefix = T
|
||||
dotnet_naming_style.tpascalcase.required_suffix =
|
||||
dotnet_naming_style.tpascalcase.word_separator =
|
||||
dotnet_naming_style.tpascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style._camelcase.required_prefix = _
|
||||
dotnet_naming_style._camelcase.required_suffix =
|
||||
dotnet_naming_style._camelcase.word_separator =
|
||||
dotnet_naming_style._camelcase.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.camelcase.required_prefix =
|
||||
dotnet_naming_style.camelcase.required_suffix =
|
||||
dotnet_naming_style.camelcase.word_separator =
|
||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.s_camelcase.required_prefix = s_
|
||||
dotnet_naming_style.s_camelcase.required_suffix =
|
||||
dotnet_naming_style.s_camelcase.word_separator =
|
||||
dotnet_naming_style.s_camelcase.capitalization = camel_case
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
use flake "$FLAKE#netdaemon"
|
||||
cp -rf ./HomeAssistantGenerated ./HomeAssistantGenerated.cs
|
|
@ -1,4 +0,0 @@
|
|||
obj
|
||||
bin
|
||||
NetDaemonCodegen
|
||||
HomeAssistantGenerated.cs
|
|
@ -1 +0,0 @@
|
|||
24.51.0
|
Binary file not shown.
|
@ -1,50 +0,0 @@
|
|||
using System;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.HassModel;
|
||||
using NetDaemon.HassModel.Integration;
|
||||
|
||||
using NetDaemonConfig.Apps.Spotify.Types;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.PauseUnpause
|
||||
{
|
||||
public record PauseUnpauseData(bool pause);
|
||||
|
||||
[NetDaemonApp]
|
||||
public class PlaySong
|
||||
{
|
||||
public PlaySong(IHaContext ha, Services services)
|
||||
{
|
||||
ha.RegisterServiceCallBack<PauseUnpauseData>(
|
||||
"spotify_pause_unpause",
|
||||
(e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.pause)
|
||||
{
|
||||
services.Spotifyplus.PlayerMediaPause(
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.Spotifyplus.PlayerMediaResume(
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
services.Notify.PersistentNotification(
|
||||
message: error.Message,
|
||||
title: "Erreur Spotify");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
using FuzzySharp;
|
||||
using FuzzySharp.Extractor;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.HassModel;
|
||||
using NetDaemon.HassModel.Integration;
|
||||
|
||||
using NetDaemonConfig.Apps.Spotify.Types;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
|
||||
{
|
||||
public record PlayAlbumData(string? artist, string? album);
|
||||
|
||||
[NetDaemonApp]
|
||||
public class PlayAlbum
|
||||
{
|
||||
private readonly CultureInfo _cultureInfo = new("fr-CA", false);
|
||||
|
||||
// Snake-case json options
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
|
||||
public PlayAlbum(IHaContext ha, Services services)
|
||||
{
|
||||
ha.RegisterServiceCallBack<PlayAlbumData>(
|
||||
"spotify_play_album",
|
||||
async (e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string uri;
|
||||
|
||||
if (e.artist is not null)
|
||||
{
|
||||
SpotifyplusSearchArtistsResponse? artistResult = (
|
||||
await services.Spotifyplus.SearchArtistsAsync(
|
||||
criteria: e?.artist ??
|
||||
throw new TargetException($"The artist {e?.artist} could not be found."),
|
||||
limitTotal: 1,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
// My Defaults
|
||||
market: "CA",
|
||||
includeExternal: "audio"
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusSearchArtistsResponse>(_jsonOptions);
|
||||
|
||||
string artistId = artistResult?.Result?.Items?[0]?.Id ??
|
||||
throw new TargetException($"The artist {e?.artist} could not be found.");
|
||||
|
||||
SpotifyPlusGetArtistAlbumsResponse? result = (
|
||||
await services.Spotifyplus.GetArtistAlbumsAsync(
|
||||
artistId: artistId,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
market: "CA"
|
||||
)
|
||||
).Value.Deserialize<SpotifyPlusGetArtistAlbumsResponse>(_jsonOptions);
|
||||
|
||||
List<ArtistAlbumItem> albums = result?.Result?.Items ??
|
||||
throw new TargetException($"No albums found for artist {e.artist}");
|
||||
|
||||
ExtractedResult<ArtistAlbumItem> match = Process.ExtractOne(
|
||||
new ArtistAlbumItem { Name = e.album?.ToLower(_cultureInfo) },
|
||||
albums,
|
||||
new Func<ArtistAlbumItem, string>((item) =>
|
||||
(item.Name ?? "").ToLower(_cultureInfo))
|
||||
);
|
||||
|
||||
uri = match.Value?.Uri ??
|
||||
throw new TargetException($"No matches found for album {e.album}");
|
||||
}
|
||||
else
|
||||
{
|
||||
SpotifyplusSearchAlbumsResponse? result = (
|
||||
await services.Spotifyplus.SearchAlbumsAsync(
|
||||
criteria: $"{e?.album}",
|
||||
limitTotal: 1,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
// My Defaults
|
||||
market: "CA",
|
||||
includeExternal: "audio"
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusSearchAlbumsResponse>(_jsonOptions);
|
||||
|
||||
uri = result?.Result?.Items?[0]?.Uri ??
|
||||
throw new TargetException(
|
||||
$"The album {e?.album}{(e?.artist is null ? "" : $" by {e?.artist}")} could not be found."
|
||||
);
|
||||
}
|
||||
|
||||
services.Spotifyplus.PlayerMediaPlayContext(
|
||||
contextUri: uri,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId,
|
||||
// My Defaults
|
||||
positionMs: 0,
|
||||
delay: 0.50
|
||||
);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
services.Notify.PersistentNotification(
|
||||
message: error.Message,
|
||||
title: "Erreur Spotify");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.HassModel;
|
||||
using NetDaemon.HassModel.Integration;
|
||||
|
||||
using NetDaemonConfig.Apps.Spotify.Types;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.PlayArtist
|
||||
{
|
||||
public record PlayArtistData(string? artist);
|
||||
|
||||
[NetDaemonApp]
|
||||
public class PlayArtist
|
||||
{
|
||||
// Snake-case json options
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
|
||||
public PlayArtist(IHaContext ha, Services services)
|
||||
{
|
||||
ha.RegisterServiceCallBack<PlayArtistData>(
|
||||
"spotify_play_artist",
|
||||
async (e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SpotifyplusSearchArtistsResponse? result = (
|
||||
await services.Spotifyplus.SearchArtistsAsync(
|
||||
criteria: e?.artist ?? throw new TargetException($"The artist {e?.artist} could not be found."),
|
||||
limitTotal: 1,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
// My Defaults
|
||||
market: "CA",
|
||||
includeExternal: "audio"
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusSearchArtistsResponse>(_jsonOptions);
|
||||
|
||||
string uri = result?.Result?.Items?[0]?.Uri ??
|
||||
throw new TargetException($"The artist {e?.artist} could not be found.");
|
||||
|
||||
services.Spotifyplus.PlayerMediaPlayContext(
|
||||
contextUri: uri,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId,
|
||||
// My Defaults
|
||||
positionMs: 0,
|
||||
delay: 0.50
|
||||
);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
services.Notify.PersistentNotification(
|
||||
message: error.Message,
|
||||
title: "Erreur Spotify");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
using FuzzySharp;
|
||||
using FuzzySharp.Extractor;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.HassModel;
|
||||
using NetDaemon.HassModel.Integration;
|
||||
|
||||
using NetDaemonConfig.Apps.Spotify.Types;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
|
||||
{
|
||||
public record PlayPlaylistData(string? playlist);
|
||||
|
||||
[NetDaemonApp]
|
||||
public class PlayPlaylist
|
||||
{
|
||||
private readonly CultureInfo _cultureInfo = new("fr-CA", false);
|
||||
|
||||
// Snake-case json options
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
|
||||
public PlayPlaylist(IHaContext ha, Services services)
|
||||
{
|
||||
ha.RegisterServiceCallBack<PlayPlaylistData>(
|
||||
"spotify_play_playlist",
|
||||
async (e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string query = e?.playlist ?? throw new TargetException("Query not found.");
|
||||
|
||||
SpotifyplusPlaylistResponse? result = (
|
||||
await services.Spotifyplus.GetPlaylistFavoritesAsync(
|
||||
limitTotal: 200,
|
||||
sortResult: true,
|
||||
entityId: Globals.DefaultEntityId
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusPlaylistResponse>(_jsonOptions);
|
||||
|
||||
List<PlaylistsItem> myPlaylists = result?.Result?.Items ??
|
||||
throw new TargetException($"No playlists found for query {query}");
|
||||
|
||||
ExtractedResult<PlaylistsItem> match = Process.ExtractOne(
|
||||
new PlaylistsItem { Name = query.ToLower(_cultureInfo) },
|
||||
myPlaylists,
|
||||
new Func<PlaylistsItem, string>((item) => (item.Name ?? "").ToLower(_cultureInfo))
|
||||
);
|
||||
|
||||
string uri = match.Value?.Uri ?? throw new TargetException($"No matches found for query {query}");
|
||||
|
||||
// We search outside the user's playlists if the score is too low
|
||||
if (match.Score < 85)
|
||||
{
|
||||
SpotifyplusPlaylistResponse? otherResult = (
|
||||
await services.Spotifyplus.SearchPlaylistsAsync(
|
||||
criteria: query,
|
||||
limitTotal: 1,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
// My Defaults
|
||||
market: "CA",
|
||||
includeExternal: "audio"
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusPlaylistResponse>(_jsonOptions);
|
||||
|
||||
string potentialUri = otherResult?.Result?.Items?[0]?.Uri ??
|
||||
throw new TargetException($"No public matches found for query {query}");
|
||||
|
||||
uri = potentialUri;
|
||||
}
|
||||
|
||||
services.Spotifyplus.PlayerMediaPlayContext(
|
||||
contextUri: uri,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId,
|
||||
// My Defaults
|
||||
positionMs: 0,
|
||||
delay: 0.50
|
||||
);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
services.Notify.PersistentNotification(
|
||||
message: error.Message,
|
||||
title: "Erreur Spotify");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.HassModel;
|
||||
using NetDaemon.HassModel.Integration;
|
||||
|
||||
using NetDaemonConfig.Apps.Spotify.Types;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.PlaySong
|
||||
{
|
||||
public record PlaySongData(string? artist, string? song);
|
||||
|
||||
[NetDaemonApp]
|
||||
public class PlaySong
|
||||
{
|
||||
// Snake-case json options
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
|
||||
public PlaySong(IHaContext ha, Services services)
|
||||
{
|
||||
ha.RegisterServiceCallBack<PlaySongData>(
|
||||
"spotify_play_song",
|
||||
async (e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SpotifyplusSearchTracksResponse? result = (
|
||||
await services.Spotifyplus.SearchTracksAsync(
|
||||
criteria: $"{e?.artist} {e?.song}",
|
||||
limitTotal: 1,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
// My Defaults
|
||||
market: "CA",
|
||||
includeExternal: "audio"
|
||||
)
|
||||
).Value.Deserialize<SpotifyplusSearchTracksResponse>(_jsonOptions);
|
||||
|
||||
string uri = result?.Result?.Items?[0]?.Uri ?? throw new TargetException(
|
||||
$"The song {e?.song}{(e?.artist is null ? "" : $" by {e?.artist}")} could not be found."
|
||||
);
|
||||
|
||||
services.Spotifyplus.PlayerMediaPlayTracks(
|
||||
uris: uri,
|
||||
entityId: Globals.DefaultEntityId,
|
||||
deviceId: Globals.DefaultDevId,
|
||||
// My Defaults
|
||||
positionMs: 0,
|
||||
delay: 0.50
|
||||
);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
services.Notify.PersistentNotification(
|
||||
message: error.Message,
|
||||
title: "Erreur Spotify");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public record ArtistAlbumItem
|
||||
{
|
||||
public string? AlbumType { get; set; }
|
||||
public List<Artist>? Artists { get; set; }
|
||||
public List<object>? AvailableMarkets { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public List<Image>? Images { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? ReleaseDate { get; set; }
|
||||
public string? ReleaseDatePrecision { get; set; }
|
||||
public Restrictions? Restrictions { get; set; }
|
||||
public int? TotalTracks { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record ArtistAlbumResult
|
||||
{
|
||||
public double? DateLastRefreshed { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public int? Limit { get; set; }
|
||||
public object? Next { get; set; }
|
||||
public int? Offset { get; set; }
|
||||
public object? Previous { get; set; }
|
||||
public int? Total { get; set; }
|
||||
public List<ArtistAlbumItem>? Items { get; set; }
|
||||
}
|
||||
|
||||
public record SpotifyPlusGetArtistAlbumsResponse
|
||||
{
|
||||
public UserProfile? UserProfile { get; set; }
|
||||
public ArtistAlbumResult? Result { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public static class Globals
|
||||
{
|
||||
public const string DefaultDevId = "homie";
|
||||
public const string DefaultEntityId = "media_player.spotifyplus";
|
||||
}
|
||||
|
||||
// https://jsonformatter.org/yaml-to-json
|
||||
// https://json2csharp.com
|
||||
// https://github.com/thlucas1/homeassistantcomponent_spotifyplus/blob/master/custom_components/spotifyplus/services.yaml
|
||||
public record Restrictions { }
|
||||
|
||||
public record UserProfile
|
||||
{
|
||||
public string? Country { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? Product { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record ExternalUrls
|
||||
{
|
||||
public string? Spotify { get; set; }
|
||||
}
|
||||
|
||||
public record Followers
|
||||
{
|
||||
public string? Href { get; set; }
|
||||
public int? Total { get; set; }
|
||||
}
|
||||
|
||||
public record Image
|
||||
{
|
||||
public string? Url { get; set; }
|
||||
public int? Height { get; set; }
|
||||
public int? Width { get; set; }
|
||||
}
|
||||
|
||||
public record Owner
|
||||
{
|
||||
public string? DisplayName { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public Followers? Followers { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record Tracks
|
||||
{
|
||||
public string? Href { get; set; }
|
||||
public int? Total { get; set; }
|
||||
}
|
||||
|
||||
public record Artist
|
||||
{
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record ExternalIds
|
||||
{
|
||||
public object? Ean { get; set; }
|
||||
public string? Isrc { get; set; }
|
||||
public object? Upc { get; set; }
|
||||
}
|
||||
|
||||
public record Album
|
||||
{
|
||||
public string? AlbumType { get; set; }
|
||||
public List<Artist>? Artists { get; set; }
|
||||
public List<string>? AvailableMarkets { get; set; }
|
||||
public List<object>? Copyrights { get; set; }
|
||||
public ExternalIds? ExternalIds { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public List<object>? Genres { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public List<Image>? Images { get; set; }
|
||||
public object? Label { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public object? Popularity { get; set; }
|
||||
public string? ReleaseDate { get; set; }
|
||||
public string? ReleaseDatePrecision { get; set; }
|
||||
public Restrictions? Restrictions { get; set; }
|
||||
public int? TotalTracks { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public record PlaylistsItem
|
||||
{
|
||||
public bool? Collaborative { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public List<Image>? Images { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public Owner? Owner { get; set; }
|
||||
public bool? Public { get; set; }
|
||||
public string? SnapshotId { get; set; }
|
||||
public Tracks? Tracks { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record PlaylistsResult
|
||||
{
|
||||
public string? Href { get; set; }
|
||||
public int? Limit { get; set; }
|
||||
public object? Next { get; set; }
|
||||
public int? Offset { get; set; }
|
||||
public object? Previous { get; set; }
|
||||
public int? Total { get; set; }
|
||||
public List<PlaylistsItem>? Items { get; set; }
|
||||
}
|
||||
|
||||
public record SpotifyplusPlaylistResponse
|
||||
{
|
||||
public UserProfile? UserProfile { get; set; }
|
||||
public PlaylistsResult? Result { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public record AlbumItem
|
||||
{
|
||||
public string? AlbumType { get; set; }
|
||||
public List<Artist>? Artists { get; set; }
|
||||
public List<string>? AvailableMarkets { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public List<Image>? Images { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? ReleaseDate { get; set; }
|
||||
public string? ReleaseDatePrecision { get; set; }
|
||||
public Restrictions? Restrictions { get; set; }
|
||||
public int? TotalTracks { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record AlbumResult
|
||||
{
|
||||
public string? Href { get; set; }
|
||||
public int? Limit { get; set; }
|
||||
public string? Next { get; set; }
|
||||
public int? Offset { get; set; }
|
||||
public object? Previous { get; set; }
|
||||
public int? Total { get; set; }
|
||||
public List<AlbumItem>? Items { get; set; }
|
||||
}
|
||||
|
||||
public record SpotifyplusSearchAlbumsResponse
|
||||
{
|
||||
public UserProfile? UserProfile { get; set; }
|
||||
public AlbumResult? Result { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public record ArtistItem
|
||||
{
|
||||
public ExternalUrls? ExternalUrls { get; init; }
|
||||
public Followers? Followers { get; init; }
|
||||
public List<string>? Genres { get; init; }
|
||||
public string? Href { get; init; }
|
||||
public string? Id { get; init; }
|
||||
public string? ImageUrl { get; init; }
|
||||
public List<Image>? Images { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public int? Popularity { get; init; }
|
||||
public string? Type { get; init; }
|
||||
public string? Uri { get; init; }
|
||||
}
|
||||
|
||||
public record ArtistResult
|
||||
{
|
||||
public string? Href { get; init; }
|
||||
public int? Limit { get; init; }
|
||||
public string? Next { get; init; }
|
||||
public int? Offset { get; set; }
|
||||
public object? Previous { get; init; }
|
||||
public int? Total { get; init; }
|
||||
public List<ArtistItem>? Items { get; init; }
|
||||
}
|
||||
|
||||
public record SpotifyplusSearchArtistsResponse
|
||||
{
|
||||
public UserProfile? UserProfile { get; init; }
|
||||
public ArtistResult? Result { get; init; }
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace NetDaemonConfig.Apps.Spotify.Types
|
||||
{
|
||||
public record SongItem
|
||||
{
|
||||
public Album? Album { get; set; }
|
||||
public List<Artist>? Artists { get; set; }
|
||||
public List<string>? AvailableMarkets { get; set; }
|
||||
public int? DiscNumber { get; set; }
|
||||
public int? DurationMs { get; set; }
|
||||
public bool? Explicit { get; set; }
|
||||
public ExternalIds? ExternalIds { get; set; }
|
||||
public ExternalUrls? ExternalUrls { get; set; }
|
||||
public string? Href { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public bool? IsLocal { get; set; }
|
||||
public object? IsPlayable { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public int? Popularity { get; set; }
|
||||
public object? PreviewUrl { get; set; }
|
||||
public Restrictions? Restrictions { get; set; }
|
||||
public int? TrackNumber { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
}
|
||||
|
||||
public record SongResult
|
||||
{
|
||||
public string? Href { get; set; }
|
||||
public int? Limit { get; set; }
|
||||
public string? Next { get; set; }
|
||||
public int? Offset { get; set; }
|
||||
public object? Previous { get; set; }
|
||||
public int? Total { get; set; }
|
||||
public List<SongItem>? Items { get; set; }
|
||||
}
|
||||
|
||||
public record SpotifyplusSearchTracksResponse
|
||||
{
|
||||
public UserProfile? UserProfile { get; set; }
|
||||
public SongResult? Result { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft": "Warning"
|
||||
},
|
||||
"ConsoleThemeType": "Ansi"
|
||||
},
|
||||
"HomeAssistant": {
|
||||
"Host": "homie.nelim.org",
|
||||
"Port": 443,
|
||||
"Ssl": true
|
||||
},
|
||||
"NetDaemon": {
|
||||
"ApplicationConfigurationFolder": "./apps"
|
||||
},
|
||||
"CodeGeneration": {
|
||||
"Namespace": "HomeAssistantGenerated",
|
||||
"OutputFile": "HomeAssistantGenerated.cs",
|
||||
"UseAttributeBaseClasses" : "false"
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (builtins) attrValues replaceStrings;
|
||||
inherit (config.sops) secrets;
|
||||
|
||||
inherit (pkgs.callPackage ./package.nix {}) netdaemonConfig;
|
||||
in {
|
||||
khepri.compositions."netdaemon" = {
|
||||
networks.netdaemon = {external = true;};
|
||||
|
||||
services."netdaemon5" = {
|
||||
image = import ./images/netdaemon.nix pkgs;
|
||||
restart = "always";
|
||||
|
||||
environmentFiles = [secrets.netdaemon.path];
|
||||
environment = {
|
||||
HomeAssistant__Host = "homie.nelim.org";
|
||||
HomeAssistant__Port = "443";
|
||||
HomeAssistant__Ssl = "true";
|
||||
NetDaemon__ApplicationAssembly = "netdaemon.dll";
|
||||
Logging__LogLevel__Default = "Information"; # use Information/Debug/Trace/Warning/Error
|
||||
TZ = "America/New_York";
|
||||
};
|
||||
|
||||
volumes = ["${netdaemonConfig}:/data"];
|
||||
networks = ["netdaemon"];
|
||||
};
|
||||
};
|
||||
|
||||
services.home-assistant = {
|
||||
customComponents = attrValues {
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.hass-components)
|
||||
netdaemon
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = let
|
||||
nixFetchDeps =
|
||||
replaceStrings [(toString self)] ["$FLAKE"]
|
||||
#nix
|
||||
''
|
||||
let
|
||||
config = (builtins.getFlake ("$FLAKE")).nixosConfigurations.homie;
|
||||
inherit (config) pkgs;
|
||||
|
||||
netdaemonConfig = pkgs.callPackage ${toString ./package.nix} {};
|
||||
in
|
||||
netdaemonConfig.fetch-deps
|
||||
'';
|
||||
in [
|
||||
(pkgs.writeShellApplication {
|
||||
name = "bumpNetdaemonDeps";
|
||||
|
||||
runtimeInputs = with pkgs; [
|
||||
dos2unix
|
||||
dotnet-sdk_9
|
||||
];
|
||||
|
||||
text = ''
|
||||
# Install codegen
|
||||
dotnet tool install --create-manifest-if-needed NetDaemon.HassModel.CodeGen --version "$(cat ./.version)"
|
||||
|
||||
# Run it
|
||||
dotnet tool run nd-codegen -token "$(sed 's/HomeAssistant__Token=//' ${secrets.netdaemon.path})"
|
||||
dos2unix ./HomeAssistantGenerated.cs
|
||||
|
||||
# This is to not have it count towards CSharp in the repo
|
||||
mv ./HomeAssistantGenerated.cs ./HomeAssistantGenerated
|
||||
|
||||
# Update all nugets to latest versions
|
||||
regex='PackageReference Include="([^"]*)" Version="([^"]*)"'
|
||||
|
||||
find . -type f -name '*.csproj' | while read -r file; do
|
||||
# Extract unique package names from the .csproj file
|
||||
packages=$(grep -oP "$regex" "$file" | sed -E 's/.*Include="([^"]*)".*/\1/' | sort -u)
|
||||
|
||||
# Loop through each package and update
|
||||
for package in $packages; do
|
||||
echo -e "\033[35mUpdate $file package: $package\033[0m"
|
||||
dotnet add "$file" package "$package"
|
||||
done
|
||||
done
|
||||
|
||||
$(nix build --no-link --print-out-paths --impure --expr "$(cat <<EOF
|
||||
${nixFetchDeps}
|
||||
EOF
|
||||
)") .
|
||||
|
||||
alejandra -q .
|
||||
rm -r "$FLAKE/.config"
|
||||
|
||||
sed -i "s/finalImageTag = .*/finalImageTag = \"$(cat ./.version)\";/" ./images/netdaemon.nix
|
||||
updateImages .
|
||||
'';
|
||||
})
|
||||
];
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
[
|
||||
{
|
||||
"pname": "Cronos",
|
||||
"version": "0.9.0",
|
||||
"hash": "sha256-yDYBfqSXqvT/VPUf6UT3XOgqqPmOMYqhjCBxpF5i15c="
|
||||
},
|
||||
{
|
||||
"pname": "FuzzySharp",
|
||||
"version": "2.0.2",
|
||||
"hash": "sha256-GuWqVOo+AG8MSvIbusLPjKfJFQRJhSSJ9eGWljTBA/c="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.Analyzers",
|
||||
"version": "3.3.4",
|
||||
"hash": "sha256-qDzTfZBSCvAUu9gzq2k+LOvh6/eRvJ9++VCNck/ZpnE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.Common",
|
||||
"version": "4.12.0",
|
||||
"hash": "sha256-mm/OKG3zPLAeTVGZtuLxSG+jpQDOchn1oyHqBBJW2Ho="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.CSharp",
|
||||
"version": "4.12.0",
|
||||
"hash": "sha256-m1i1Q5pyEq4lAoYjNE9baEjTplH8+bXx5wSA+eMmehk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-uBLeb4z60y8z7NelHs9uT3cLD6wODkdwyfJm6/YZLDM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-xtG2USC9Qm0f2Nn6jkcklpyEDT3hcEZOxOwTc0ep7uc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Binder",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-6ajYWcNOQX2WqftgnoUmVtyvC1kkPOtTCif4AiKEffU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.CommandLine",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-RE6DotU1FM1sy5p3hukT+WOFsDYJRsKX6jx5vhlPceM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.EnvironmentVariables",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-tDJx2prYZpr0RKSwmJfsK9FlUGwaDmyuSz2kqQxsWoI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.FileExtensions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-PsLo6mrLGYfbi96rfCG8YS1APXkUXBG4hLstpT60I4s="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Json",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-qQn7Ol0CvPYuyecYWYBkPpTMdocO7I6n+jXQI2udzLI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.UserSecrets",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-GoEk+Qq7lbiwWurHYx1LkDaUzIpOzaoTiVGDPfViGak="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyModel",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-xirwlMWM0hBqgTneQOGkZ8l45mHT08XuSSRIbprgq94="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Diagnostics",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-JMbhtjdcWRlrcrbgPlowfj26+pM+MYhnPIaYKnv9byU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Diagnostics.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-wG1LcET+MPRjUdz3HIOTHVEnbG/INFJUqzPErCM79eY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileProviders.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-mVfLjZ8VrnOQR/uQjv74P2uEG+rgW72jfiGdSZhIfDc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileProviders.Physical",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-IzFpjKHmF1L3eVbFLUZa2N5aH3oJkJ7KE1duGIS7DP8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileSystemGlobbing",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-eBLa8pW/y/hRj+JbEr340zbHRABIeFlcdqE0jf5/Uhc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Hosting",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-apIN4Cz86ujsMp/ibxcvguA9uCFaFqOsZ4kAUPX5ASI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Hosting.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-NhEDqZGnwCDFyK/NKn1dwLQExYE82j1YVFcrhXVczqY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Http",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-MsStH3oUfyBbcSEoxm+rfxFBKI/rtB5PZrSGvtDjVe0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Abstractions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Configuration",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-ysPjBq64p6JM4EmeVndryXnhLWHYYszzlVpPxRWkUkw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Console",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-N2t9EUdlS6ippD4Z04qUUyBuQ4tKSR/8TpmKScb5zRw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Debug",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-5W6fP9Eb98U3MTWKeLzSNl2cRFpE694OOPjpWp/qTAk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.EventLog",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-mIL1I85Ef5+/mXl24odoUpcXet+jCZTRtKCd5z6YUwI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.EventSource",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-pplZskMsR3gGbs3I0wycGsvIMPIpfWFJpOsR9GkiYRw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options.ConfigurationExtensions",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-r1Z3sEVSIjeH2UKj+KMj86har68g/zybSqoSjESBcoA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Primitives",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.AppModel",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-gZGv0mo8Y3c/ppfRDfQq0aLTepgmnOMOhVMTnl7qOyQ="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.AppModel.SourceDeployedApps",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-NHsDb7ChPYC6+QBACvi2mFsa6VAJqEh1qegA8RHY3TU="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.Client",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-E4B61fI9DuczvxvkYEr9QJFaKNYg2vfVk3awXSBMaW4="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.Extensions.Logging",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-Jvk0shJ1ld+twM6KqlSdZZjGfaNfCFMgD6SHEMIzXAw="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.Extensions.Scheduling",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-TlrsICPnrUqfpVxFtmXxdZPeB3u6LGJDLb8ZULcYIYA="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.Extensions.Tts",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-0Qh5Rs7oGCu3IWa8c4EV9j5RgLE+MJAA1PVDUqs3Zvg="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.HassModel",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-d9SP2Neh8any4xxdpngpCy/OBGMHdjmIRCiDaCkjXpM="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.HassModel.Integration",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-SelTI+6nD9+MEtWG1IdL+g8DFiIBwv0TuXiLORsVmZI="
|
||||
},
|
||||
{
|
||||
"pname": "NetDaemon.Runtime",
|
||||
"version": "24.51.0",
|
||||
"hash": "sha256-VCEBzcJmMbabUnnddyAP/lua/q412WQP38ZoTdXDaXg="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog",
|
||||
"version": "4.0.0",
|
||||
"hash": "sha256-j8hQ5TdL1TjfdGiBO9PyHJFMMPvATHWN1dtrrUZZlNw="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog",
|
||||
"version": "4.2.0",
|
||||
"hash": "sha256-7f3EpCsEbDxXgsuhE430KVI14p7oDUuCtwRpOCqtnbs="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.AspNetCore",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-h58CFtXBRvwhTCrhQPHQMKbp98YiK02o+cOyOmktVpQ="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Extensions.Hosting",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-bidr2foe7Dp4BJOlkc7ko0q6vt9ITG3IZ8b2BKRa0pw="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Extensions.Logging",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-aGkz1V4HVl0rWC1BkcnLhG1EC7WLBoT3tdLdUUTFXaw="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Formatting.Compact",
|
||||
"version": "3.0.0",
|
||||
"hash": "sha256-nejEYqJEMG9P2iFZvbsCUPr5LZRtxbdUTLCI9N71jHY="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Settings.Configuration",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-Q/q5UiSrcxoy5a/orod20E2RfiRtHDhxjjGMe1dW35I="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Sinks.Console",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-QH8ykDkLssJ99Fgl+ZBFBr+RQRl0wRTkeccQuuGLyro="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Sinks.Debug",
|
||||
"version": "3.0.0",
|
||||
"hash": "sha256-7/LmoRF1rUDFhJ47bTRQQFRgSHnZDO8484r3sCGqYvE="
|
||||
},
|
||||
{
|
||||
"pname": "Serilog.Sinks.File",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-KQmlUpG9ovRpNqKhKe6rz3XMLUjkBqjyQhEm2hV5Sow="
|
||||
},
|
||||
{
|
||||
"pname": "System.Collections.Immutable",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.EventLog",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-tPvt6yoAp56sK/fe+/ei8M65eavY2UUhRnbrREj/Ems="
|
||||
},
|
||||
{
|
||||
"pname": "System.IO.Pipelines",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-vb0NrPjfEao3kfZ0tavp2J/29XnsQTJgXv3/qaAwwz0="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reactive",
|
||||
"version": "6.0.1",
|
||||
"hash": "sha256-Lo5UMqp8DsbVSUxa2UpClR1GoYzqQQcSxkfyFqB/d4Q="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE="
|
||||
},
|
||||
{
|
||||
"pname": "YamlDotNet",
|
||||
"version": "16.2.1",
|
||||
"hash": "sha256-Nu/rD43sihE4PTHC5r2Ua2gafclqcd2U95RcNFvGFhc="
|
||||
}
|
||||
]
|
|
@ -1,8 +0,0 @@
|
|||
pkgs:
|
||||
pkgs.dockerTools.pullImage rec {
|
||||
imageName = "netdaemon/netdaemon5";
|
||||
imageDigest = "sha256:983727221f52dba8375b5e90fe5e4e660e88fa5f2fe67cdd1141c3c7643b5ba7";
|
||||
hash = "sha256-kf7fP7x0PUaMivcLNOZ+3oH3d+NrpyCy74N7nlshywU=";
|
||||
finalImageName = imageName;
|
||||
finalImageTag = "24.51.0";
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>13.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>NetDaemonConfig</RootNamespace>
|
||||
<Version>$([System.IO.File]::ReadAllText(".version"))</Version>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="$(MSBuildProjectDir)appsettings.Development.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Update="$(MSBuildProjectDir)appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Target Name="AfterPublishMessage" AfterTargets="Publish">
|
||||
<Message Text="Publish done!" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NetDaemon.AppModel" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.AppModel.SourceDeployedApps" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.Runtime" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.HassModel" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.HassModel.Integration" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.Client" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.Extensions.Scheduling" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.Extensions.Logging" Version="24.51.0" />
|
||||
<PackageReference Include="NetDaemon.Extensions.Tts" Version="24.51.0" />
|
||||
<PackageReference Include="FuzzySharp" Version="2.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
lib,
|
||||
buildDotnetModule,
|
||||
dotnetCorePackages,
|
||||
}: let
|
||||
inherit (lib) any hasInfix hasSuffix removeSuffix;
|
||||
|
||||
srcDirs = ["apps"];
|
||||
srcPatterns = [".cs" ".csproj" ".json" ".version" "HomeAssistantGenerated"];
|
||||
|
||||
pname = "netdaemon-config";
|
||||
in
|
||||
buildDotnetModule {
|
||||
inherit pname;
|
||||
version = removeSuffix "\n" (builtins.readFile ./.version);
|
||||
|
||||
src = builtins.path {
|
||||
name = "netdaemon-src";
|
||||
path = ./.;
|
||||
filter = file: type:
|
||||
(type == "directory" && any (s: hasInfix s file) srcDirs)
|
||||
|| any (s: hasSuffix s file) srcPatterns;
|
||||
};
|
||||
|
||||
preBuild = ''
|
||||
mv HomeAssistantGenerated HomeAssistantGenerated.cs
|
||||
'';
|
||||
|
||||
projectFile = "netdaemon.csproj";
|
||||
nugetDeps = ./deps.json;
|
||||
|
||||
dotnet-sdk = dotnetCorePackages.sdk_9_0;
|
||||
dotnet-runtime = dotnetCorePackages.runtime_9_0;
|
||||
executables = [];
|
||||
|
||||
postFixup = ''
|
||||
cp -r $out/lib/${pname} $netdaemonConfig
|
||||
'';
|
||||
|
||||
outputs = ["out" "netdaemonConfig"];
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using HomeAssistantGenerated;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using NetDaemon.AppModel;
|
||||
using NetDaemon.Extensions.Logging;
|
||||
using NetDaemon.Extensions.Scheduler;
|
||||
using NetDaemon.Extensions.Tts;
|
||||
using NetDaemon.Runtime;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
await Host.CreateDefaultBuilder(args)
|
||||
.UseNetDaemonDefaultLogging()
|
||||
.UseNetDaemonRuntime()
|
||||
.UseNetDaemonTextToSpeech()
|
||||
.ConfigureServices(static (_, services) =>
|
||||
services
|
||||
.AddAppsFromAssembly(Assembly.GetExecutingAssembly())
|
||||
.AddNetDaemonStateManager()
|
||||
.AddNetDaemonScheduler()
|
||||
.AddHomeAssistantGenerated()
|
||||
)
|
||||
.Build()
|
||||
.RunAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Failed to start host... {e}");
|
||||
throw;
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: {
|
||||
services.home-assistant = {
|
||||
customComponents = builtins.attrValues {
|
||||
inherit
|
||||
(self.scopedPackages.${pkgs.system}.hass-components)
|
||||
spotifyplus
|
||||
;
|
||||
};
|
||||
|
||||
extraComponents = [
|
||||
"spotify"
|
||||
];
|
||||
|
||||
customSentences."assist_spotify" = import ./sentences.nix;
|
||||
|
||||
configFiles.
|
||||
".storage/SpotifyWebApiPython_librespot_credentials.json"
|
||||
.source = config.sops.secrets.spotifyd.path;
|
||||
|
||||
config.intent_script = {
|
||||
PlayAlbum = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_play_album";
|
||||
data = {
|
||||
artist = "{{ artist }}";
|
||||
album = "{{ album }}";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
PlayArtist = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_play_artist";
|
||||
data.artist = "{{ artist }}";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
PlayPlaylist = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_play_playlist";
|
||||
data.playlist = "{{ playlist }}";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
PlaySong = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_play_song";
|
||||
data = {
|
||||
artist = "{{ artist }}";
|
||||
song = "{{ song }}";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
Pause = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_pause_unpause";
|
||||
data.pause = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
Unpause = {
|
||||
async_action = false;
|
||||
action = [
|
||||
{
|
||||
service = "netdaemon.spotify_pause_unpause";
|
||||
data.pause = false;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue