Skip to content
QuackHack-McBlindyPublic

Latest commit

ย 

History

821 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

โ„๏ธ๐Ÿฆฏ๐Ÿฆ† QuackHack-McBLindy NixOS dotfiles

CI License NixOS Nix Linux Kernel GNOME Bash Python Rust yo zigduck

This is a automagiduckically updated README.md

Sponsors Buy Me a Coffee

QuackHack-McBlindy

Caution

Do not blindly run this flake.
That's my job. ๐Ÿง‘โ€๐Ÿฆฏ
QuackHack-McBLindy.com

QuackHack-McBlindy

Here lives all home machines/phone config files,
and home automations, fully reproducible,
crafted as a tiny Nix flake
Glued together by a Nix-flavoured command line utility,
easiy expanded and used to deploy, doc, and duck around

What Makes This Configuration Unique?

I manage my own homes.
Custom everything.

โŒ Home Manager No ducking way โ€” I just auto-symlink ./home ---> /home
๐Ÿ’ฌ Grammar & Regex Compiled at build-time from ./bin <---
โŒ Home Assistant Quack no - 19/19 home automations written using the compiled grammar

This flake holds

Category Contents
๐ŸŽ™๏ธ Execution Scripts Voice Scripts Voice Patterns Phrases
๐ŸŒ Network WireGuard
๐Ÿ–ฅ๏ธ Infrastructure Hosts Phones ESP32 Packages DevShells
๐Ÿ  Home Automation TVs Channels Devices Scenes
๐Ÿ” Security Yubikeys Secrets
๐Ÿš€ Workflows Auto--Docs Auto--Deploy
๐ŸŽ‰ ??? Pokรฉmon Ducks

List would get long, very quackly.
perhaps a more suitable question would be:
"What makes this configuration common?"


honorable mentions in my flake inputs:

๐Ÿ”’ sops-nix awesome secret keepin'
๐Ÿ” magnixify makes me see screen
๐Ÿ—ฃ๏ธ yo holds my scripts and grammar, runs the voice pipeline
๐Ÿฆ† zigduck automates my house
๐Ÿ”ด PokรฉNix builds & deploys my Pokรฉmon collection

โ„๏ธ Flake

Define yourself at `config.this.user.me`.
{
  discord = "https://discordapp.com/users/675530282849533952";
  dotfilesDir = "/home/pungkula/dotfiles";
  email = "isthisrandomenough@protonmail.com";
  extraGroups = [ "networkmanager" "wheel" "dialout" "docker" "dockeruser" "zigduck" "users" "pungkula" "adbusers" "audio" "2000" "i2c" "input" "mqtt" ];
  hashedPassword = "$y$j9T$m8hPD36i1VMaO5rurbZ4j0$KpzQyat.F6NoWFKpisEj77TvpN2wBGB8ezd26QoKDj6";
  matrix = "https://matrix.to/#/@quackhack-mcblindy:matrix.org";
  mobileDevices =   {
    iphone =     {
      pubkey = "UFB0T1Y/uLZi3UBtEaVhCi+QYldYGcOZiF9KKurC5Hw=";
      wgip = "10.0.0.7"
    };
    tablet =     {
      pubkey = "ETRh93SQaY+Tz/F2rLAZcW7RFd83eofNcBtfyHCBWE4=";
      wgip = "10.0.0.8"
    };
    watch =     {
      pubkey = "b28LFIbZSxbCLTaK7qsLp0hAMH+yo93+rVV14qif4hY=";
      wgip = "10.0.0.9"
    }
  };
  name = "pungkula";
  repo = "git@github.com:QuackHack-McBlindy/dotfiles.git"
}
Define each hosts data at `config.this.host`.
{
  hostname = "desktop";
  interface = [ "enp119s0" ];
  ip = "192.168.1.111";
  keys =   {
    privateKeys =     {

    };
    publicKeys =     {
      adb = "QAAAACEJNfsfRV4PQ9Ah87MbTVbMkbXC6CAMDOR+0K6mIpv/4TSzYMkc2qit3Kryc55IVOjwR3fJRjj/uL549gZ7nEemWtcd3AsYQBp0iIEor8nu1L/V6jfsTY6Xe/pl06xoroy6OwZRWuDbZ4wD2xQRRQjfPd+JtYnMAWneM6r1V15uR67w4ITvjk3ckyfgNeLZMUwahMRjC3wSjaU9sAdKNmg8yPd8uHZ+mK6mstxJFAGEpnnm1lE7Z2r0DF6h6MKY1++dwhU+WM5BRDNiBg+D4i6fDW4+Z1I9ENuFnjT17zAxZXch04SNlG3O94BANYP7jmKp60OvtDL6msfphntuIUzMCkndF9De0Kv4lJdQxe1d+wf+AFpmtd/xtrk45YdMV+eWCJf2OkidaHmSj4ffkAobpun0VrkZN2Z1JymmdsvUbyMjAsby3Zun0xr3EocUS8Jy5TcsK/dcpD6CB5dqzlHhsHSAWt2TDwPzZYXgV1xc+q+PqM09OVN1xActJu75UMkg5b84U15hwQvYdwB8UaopMWWk6p064c7gxYSfH7fSxwkW2Jy1CElgJa55Pp4SZG9b/3B+VcNL1WSf6v/lvJqPbrRvBqvS0+e9wcFMNZtQKTX3n5X0wW1/czZPCQX+hmM8Uu1qrtaz4rKViIEGf4YR0/9eUGYQVfuAxAh8ZmsroJlnAAEAAQA= pungkula@desktop";
      age = "age16utg7mmk73cn3glrwthtm0p7mf6g3vrd48h3ucpn6wnf28pgxvcsh4rjjp";
      borg = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVYczAOBSeS7WfSvzYDOS4Q9Ss+yxCf2G5MVfAALOx/";
      builder = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQ7c/AeIpmJS6cWQkHOe4ZEq3DXVRnjtTWuWfx6L46n";
      cache = "cache:/pbj1Agw2OoSSDZcClS69RHa1aNcwwTOX3GIEGKYwPc=";
      host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILdwPkRQxlbrbRGwEO5zMJ4m+7QqUQPZg1iqbd5HRP34";
      iPhone = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMcmr+z7k/yCbrFg+JDgo8JCuWqNVYn10ajRbNTp8fq";
      ssh = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPwZL27kGTQDIlSe03abT9F24nSAizORyjo5cI3BD92s";
      wireguard = "Oq0ZaYAnOo5sLpV//OEFwLgjVCxPyeQqf8cZBASluWk="
    }
  };
  modules =   {
    hardware = [ "cpu/intel" "audio" ];
    networking = [ "default" "pool" ];
    programs = [ "default" "thunar" "firefox" "vesktop" ];
    services = [ "ssh" "default" "yo" "yo-client" "adb" "openrgb" "backup" "cache" "keyd" "jelly" "duckdns" "sunshine" ];
    system = [ "nix" "pkgs" "gnome" "crossEnv" "gtk" ];
    virtualisation = [ "docker" "vm" ]
  };
  system = "x86_64-linux";
  wgip = "10.0.0.2"
}
Define any optional theme configuration at `config.this.theme`.
{
  cursorTheme =   {
    name = "Bibata-Modern-Classic";
    package = "/nix/store/qf0hqvlf7falfxxx8vs67adk6m08qdmd-bibata-cursors-2.0.7";
    size = 32
  };
  enable = false;
  fonts =   {
    monospace = "Fira Code";
    packages = [ "/nix/store/n0x0mrlx0j63ak5slnvp5m21xpb0k50q-fira-code-6.2" ];
    system = "Fira Sans"
  };
  gtkSettings =   {
    gtk-application-prefer-dark-theme = "1";
    gtk-cursor-theme-name = "Bibata-Modern-Classic";
    gtk-icon-theme-name = "elementary-xfce-icon-theme"
  };
  iconTheme =   {
    name = "Papirus-Dark";
    package = "/nix/store/48m37bqwn88nj55zszg6nla57qqmm07i-papirus-icon-theme-20260801"
  };
  name = "gtk3.css";
  styles = "/nix/store/wg23gpsvsws9j5r5r1v9369pa4vfyv15-gtk3.css"
}
Define Zigbee-devices, scenes, automations, tv's, channels etc at `config.house`.

dotfiles/modules/myHouse.nix โฎž https://github.com/quackhack-mcblindy/dotfiles

# dotfiles/modules/myHouse.nix โฎž https://github.com/quackhack-mcblindy/dotfiles
#  ยป โ˜… QuackHack-McBLindy.com โ˜… ยซ
# ๐Ÿฆ† โฎž my house - qwack! entire home automation in natural language!
{ #  ยป โ˜… github.com/QuackHack-McBlindy/zigduck โ˜… ยซ
  config,
  lib,
  self,
  zigduck,
  hosts,
  devices,
  icons,
  pkgs,
  ...
} : let
  # ๐Ÿฆ† says โฎž load custom pages for da dashboard
  customPages  = import ./dashboard.nix { inherit lib config pkgs; };
  pages        = customPages.pages;
  # ๐Ÿฆ† says โฎž import ma devicez
  houseDevices    = import ./devices.nix { inherit lib config pkgs icons hosts zigduck; };
  myDevices       = houseDevices.devices;
  myZigbeeDevices = houseDevices.zigbee;

  # ๐Ÿฆ† says โฎž load css files
  css.tv     = builtins.readFile ./themes/css/duckdash/tv.css;
  css.chat   = builtins.readFile ./themes/css/duckdash/chat.css;

  # ๐Ÿฆ† says โฎž auto detect summer/winter time (for EPG)
  flakeEpoch =
    if self ? lastModified then toString self.lastModified
    else throw "flake has no lastModified โ€” needs to be a git-backed flake";
  tzProbe = pkgs.runCommand "tz-probe" {
    nativeBuildInputs = [ pkgs.tzdata ];
    env.TZDIR = "${pkgs.tzdata}/share/zoneinfo";
    env.EPOCH = flakeEpoch;
  } ''
    TZ=Europe/Stockholm date -d "@$EPOCH" +%z > $out
  '';
  tzOffset = lib.strings.trim (builtins.readFile tzProbe);
  timezone_offset_hours = {
    "+0100" = -1;
    "+0200" = -2;
  }.${tzOffset} or (throw "unexpected TZ offset from tzdata: ${tzOffset}");

  # ๐Ÿฆ† says โฎž now we can stream tts yo - from zigduck yo
  yoClientsJson = pkgs.writeText "yo-clients.json" (builtins.toJSON (
    map (c: c // { connected_at = 0; })
    [
      { room = "livingroom"; ip = hosts.desktop.ip;               gain = 1.0; }
      { room = "esp";        ip = devices.ESP32-S3-BOX-3.ip;    gain = 1.0; }
    ]
  ));

in { # ๐Ÿฆ† duck say โฎž house config
  house = {
    # ๐Ÿฆ† says โฎž ROOM CONFIGURATION
    rooms = {
      bedroom    = {
        icon     = icons.room.bedroom;
        aliases  = [ "sovrum" "sovrummet" ];
      };
      hallway    = {
        icon     = icons.room.hallway;
        aliases  = [ "hall" "hallen" "entren" ];
      };
      kitchen    = {
        icon     = icons.room.kitchen;
        aliases  = [ "kรถk" "kรถket" "matsal" "matsalen" ];
      };
      livingroom = {
        icon     = icons.room.livingroom;
        aliases  = [ "vardagsrum" "vardagsrummet" ];
      };
      wc         = {
        icon     = icons.room.wc;
        aliases  = [ "toa" "toan" "toaletten" "dass" "skithuset" ];
      };
      tv-area    = {
        entertainment = true;
        icon          = icons.tv;
      };
      other.icon      = icons.room.other;
    };

    # ๐Ÿฆ† says โฎž presence home/away state
    # ๐Ÿฆ† โฎž collected in various ways for successful detection
    presence = {
      probes      = [ devices.iphone.ip ];
      homeActions = [ "skicka en notis om ๐Ÿ  โ˜‘๏ธ BACK HOME!" ];
      awayActions = [ "skicka en notis om ๐Ÿ  โŒ LEFT HOME! samt slรคck alla lampor" ];
    };

    # ๐Ÿฆ† says โฎž media
    https.media.urlFile       = config.sops.secrets.webserver.path;
    media.root                = "/Pool"; # ๐Ÿฆ† โฎž i like pools!
    media.youtubePasswordFile = config.sops.secrets.youtube_api_key.path;
    media = {
      movies      = "${config.house.media.root}/Movies";
      tv          = "${config.house.media.root}/TV";
      music       = "${config.house.media.root}/Music";
      musicVideos = "${config.house.media.root}/Music_Videos";
      otherVideos = "${config.house.media.root}/Other_Videos";
      podcasts    = "${config.house.media.root}/Podcasts";
      audiobooks  = "${config.house.media.root}/Audiobooks";
    };

    # ๐Ÿฆ† โฎž DEVICES (non zigbee/NixOS) imported devices.nix โฎœ
    devices = myDevices;

# ๐Ÿฆ† โฎž ZIGBEE โฎœ ๐Ÿ
    zigbee = {
      # ๐Ÿฆ† โฎž DEVICES (loaded from devices.nix) โฎœ
      devices = myZigbeeDevices;

      # ๐Ÿฆ† says โฎž encrypted zigbee network key
      networkKeyFile = config.sops.secrets.z2m_network_key.path;

      # ๐Ÿฆ† says โฎž mosquitto authentication
      mosquitto = {
        host  = (lib.findFirst
              (h: lib.elem "zigduck" h.modules.services)
              (throw "NO ZIGDUCK HOST")
              (lib.attrValues hosts)).ip;
        username     = "mqtt";
        passwordFile = config.sops.secrets.mosquitto.path;
        baseTopic    = "zigduck";
      };

      # ๐Ÿฆ† says โฎž TV light syncin'
      hueSyncBox = {
        enable   = true;
        # ๐Ÿฆ† says โฎž sadly needed (i disable itz internet access - u should too)
        bridge   = {
          passwordFile = config.sops.secrets.hueBridgeAPI.path;
          # ๐Ÿฆ† says โฎž run the following to get api token:
          # curl -X POST http://192.168.1.33/api -d '{"devicetype":"house#nixos"}'
          ip           = devices.hue-bridge.ip;
        };
        syncBox = { # C42996020AAE
          # ๐Ÿฆ† says โฎž run the following to get api token for the sync box:
          # curl -k -sS -X POST "https://192.168.1.34/api/v1/registrations" -H "Content-Type: application/json" -d '{"appName": "zigduck", "instanceName": "zigduck"}'
          passwordFile = config.sops.secrets.hueSyncAPI.path;
          ip           = devices.hue-sync.ip;
          tv           = "shield";
        };
      };

      # ๐Ÿฆ†saysโฎž coordinator configuration
      coordinator = {
        vendorId  =  "10c4";
        productId = "ea60";
        symlink   = "zigbee"; # ๐Ÿฆ† says โฎž diz symlinkz da serial port to /dev/zigbee
      };

      # ๐Ÿฆ† says โฎž optional dimmer config
      dimmer.message            = "action";
      # ๐Ÿฆ† โฎž clickin' fast cyclez scnes in da dimmers room!
      dimmer.doubleClickTimeout = 500;

      # ๐Ÿฆ† says โฎž when motion triggers lights on
      motion = {
        when.dark.enable = true;
        trigger.lights = {
          after        = 14;
          before       = 9;
          duration     = 700;
          transition   = true;
        };
      };

      # ๐Ÿฆ† says โฎž when no motion triggers ALL lights off
      no.motion.trigger = {
        all.lights.off  = {
          enable  = true;
          after   = 90;  # ๐Ÿฆ† says โฎž min
          exclude = [ ]; # ๐Ÿฆ† says โฎž list of strings, devices to exclude (friendly name/alias, leaves their states as-is)
        };
      };


  # ๐Ÿฆ† โฎž AUTOMATIONS โฎœ
      automations = { # ๐Ÿฆ† says โฎž here we start quackin'
        string.prefix = "yo do";
        # ๐Ÿฆ† says โฎž there are 6 different automation types
        # ๐Ÿฆ† โฎž + a greeting automation
        greeting  = {
          enable       = true;
          awayDuration = 7200;
          door         = "Door Sensor Hall";
          delay        = 10; # ๐Ÿฆ† โฎž welcome back home idiot
          actions = [ "sรคg vรคlkommen hem idiot" ];
        };

        # ๐Ÿฆ† says โฎž 1. MQTT triggered automations
        mqtt_triggered  = {

          flash_lights  = {
            enable      = true;
            description = "Flash lights";
            topic       = "${zigduck.baseTopic}/effects/flash";
            actions  = [
              # ๐Ÿฆ† โฎž create a state snapshot and name it before flash & wait two seconds
              # ๐Ÿฆ† โฎž activate different color rich slow transition scene & wait seven seconds
              # ๐Ÿฆ† โฎž turn on all lights & wait four seconds & turn off all lights quickly as fuck & wait three secs
              # ๐Ÿฆ† โฎž turn on all lights & wait four secs & turn off all lights again quick af & wait three secs
              # ๐Ÿฆ† โฎž turn lights back on again & sleep five sec and finally restore system state to before flash
              "skapa en snapshot och dรถp den till innan flashen samt vรคnta tvรฅ sekunder"
              "tรคnd alla lampor samt vรคnta fyra sekunder"
              "slรคck alla lampor fort som fan samt vรคnta tre sekunder"
              "tรคnd alla lampor samt vรคnta fyra sekunder"
              "slรคck allt igen fort utav helvete samt vรคnta tre sekunder"
              "tรคnd allt samt vรคnta fem sekunder och slutligen รฅterstรคll systemet till innan flashen"
            ];
          };

          living_cealing = {
            enable       = true;
            description  = "Give the cealing life. Life in colors.";
            topic        = "${zigduck.baseTopic}/effects/living/cealing";
            actions  = [
              # ๐Ÿฆ† โฎž activate color rich slow transition scene & wait five seconds
              # ๐Ÿฆ† โฎž activate different color rich slow transition scene & wait seven seconds
              # ๐Ÿฆ† โฎž activate different color rich slow transition scene & wait eleven seconds
              "aktivera scen levande tak ett samt vรคnta fem sekunder"
              "aktivera scen levande tak tvรฅ samt vรคnta sju sekunder"
              "aktivera scen levande tak tre samt vรคnta elva sekunder"
            ];
          };

          loop_cealing   = {
            enable       = true;
            description  = "Give the cealing life. Life in colors.";
            topic        = "${zigduck.baseTopic}/effects/living/cealing/loop";
            actions  = [ 
            # ๐Ÿฆ† โฎž loop living cealing until state changes
            "loopa levande taket tills den รคndrar tillstรฅnd"
            ];
          };

          alarm_wakeup  = {
            enable      = true;
            description = "Time to wake up!";
            conditions  = [ { type = "someone_home"; } ];
            topic       = "${zigduck.baseTopic}/alarm/triggered";
            actions  = [ # ๐Ÿฆ† say โฎž GOOD LUCK STAYING IN BED
              # ๐Ÿฆ† โฎž notify, flash lights, TTS scream, raise the beds head & feet parts upwards at max
              # ๐Ÿฆ† โฎž wait then start blinking lights like raveparty, play sounds on all ESP32,
              # ๐Ÿฆ† โฎž wait & roll up blindz & play sum moar sounds, check motion sensors & if no motion - call the TV remote!
              "skicka en notis om โฐ ALARM: Wake up! samt flasha ljuset"
              "sรคg UPP MED DIG LATMASK!!! sedan styr sรคngens fรถtter upp samt styr sรคngens huvud uppรฅt"
              "vรคnta tio sekunder sedan blinkepartaj"
              "pinga boxen samt pinga klockan sedan vรคnta tio sekunder"
              "rulla upp persiennerna samt pinga boxen"
              "om det inte รคr nรฅgon i sovrummet sรฅ ring fjรคrrkontrollen"
            ];
          };

          timer_finish  = {
            enable      = true;
            description = "a timer is ringing";
            conditions  = [ { type = "someone_home"; } ];
            topic       = "${zigduck.baseTopic}/timer/finished";
            actions  = [ # ๐Ÿฆ† says โฎž notify, flash lights, TTS, wait and play sound on ESP32
              "skicka en notis om โฐ TIMER finish!! samt flasha ljuset"
              "sรคg timern ringer!! timern ringer!! sedan vรคnta sju sekunder samt pinga boxen"
            ];
          };


      # ๐Ÿฆ† says โฎž below are used to create cards
      # ๐Ÿฆ† says โฎž cards are read & displayed by the frontend
          # ๐Ÿฆ†sayโฎž crypto tickers
          xmr           = {
            enable      = true;
            description = "Updating XMR price data on dashboard";
            topic       = "${zigduck.baseTopic}/crypto/xmr/price";
            actions     = [ "spara current_price i xmr.json" ];
          };
          btc           = {
            enable      = true;
            description = "Updating BTC price data on dashboard";
            topic       = "${zigduck.baseTopic}/crypto/btc/price";
            actions     = [ "spara current_price i btc.json" ];
          };
          # ๐Ÿฆ†sayโฎž energy tracking
          energyPrice   = {
            enable      = true;
            description = "Updating energy data on dashboard";
            topic       = "${zigduck.baseTopic}/tibber/energy";
            actions     = [ "spara current_price i energy_price.json" ];
          };
          energyUsage   = {
            enable      = true;
            description = "Updating energy data on dashboard";
            topic       = "${zigduck.baseTopic}/tibber/energy";
            actions     = [ "spara monthly_usage i energy_usage.json" ];
          };
          # ๐Ÿฆ†sayโฎž hallway temperature
          temperature   = {
            enable      = true;
            description = "Updating temperature data on dashboard";
            topic       = "${zigduck.baseTopic}/Motion Sensor Hall";
            actions     = [ "spara temperature i temperature.json" ];
          };
          # ๐Ÿฆ†sayโฎž calendar
          calendar      = {
            enable      = true;
            description = "Updated calendar events";
            topic       = "${zigduck.baseTopic}/calendar";
            actions = [
              { type    = "shell"; command = ''
                iso_date=$(echo "$MQTT_PAYLOAD" | jq -r '.today_date')
                formatted_date=$(date -d "$iso_date" +"%b %d")
                MQTT_PAYLOAD=$(echo "$MQTT_PAYLOAD" | jq --arg d "$formatted_date" '.today_date = $d')
                echo "$MQTT_PAYLOAD" > ${zigduck.stateDir}/calendar.json
              ''; }
            ];
          };
        };

        # ๐Ÿฆ† says โฎž 2. room action automations
        room_actions = {
          hallway = { # ๐Ÿฆ† says โฎž while door is open say something fun until door closes.
            door_opened = [ "skicka en notis om ๐Ÿšช Dร–RR ร–PPEN! sedan medans dรถrren รคr รถppen sรฅ sรคg tittut! tittut!" ];
            door_closed = [];
          };

          kitchen = {
            motion_not_detected = [
              # ๐Ÿฆ† โฎž if fan is on and consuming power, start countdown to turn fan off. and slowly fade off lights.
              "om flรคkten drar mer รคn tjugo watt sรฅ stรคng flรคkten om etthundraรฅttio sekunder sedan slรคck kรถket lรฅngsamt"
            ];

            motion_detected = [
              # ๐Ÿฆ† โฎž "big" 20ms win - worth it here
              { type = "scene"; scene = "kitchenInstant"; }
              # ๐Ÿฆ† โฎž 1: start lights.
              # ๐Ÿฆ† โฎž 2: cancel the off-countdown on fan.
              # ๐Ÿฆ† โฎž 3: if my team is playin' NOW activate scene & if tv is off start the game on TV and TTS dat the game is on now
              "stรคng flรคkten om noll sekunder samt om flรคkten รคr av sรฅ sรคtt pรฅ flรคkten"
              "om bjรถrklรถven spelar just nu sรฅ aktivera scenen lรถven och om tvn รคr av sรฅ slรคng igรฅng lajv bjรถrklรถven fรถr fan plus sรคg nu bรถrjar match klant"
            ];
          };
        };

        # ๐Ÿฆ† says โฎž 3. global actions automations
        global_actions  = {
          leak_detected = [
            # ๐Ÿฆ† โฎž flash lights, notify and TTS and pling all devices with sound
            "flasha ljuset samt vรคnta sju sekunder samt tรคnd alla lampor"
            "notifiera ๐Ÿ’ง ๐Ÿšจ WATER LEAK DETECTED! samt plinga klockan samt plinga boxen"
            "sรคg VATTEN VATTEN VATTEN VATTEN samt vรคnta femton sekunder sedan sรคg VATTEN VATTEN VATTEN!"
          ];
          smoke_detected = [
            "flasha ljuset sedan vรคnta sju sekunder samt tรคnd alla lampor"
            "notifiera ๐Ÿ”ฅ ๐Ÿ”ฅ FIRE DETECTED! ๐Ÿ”ฅ ๐Ÿ”ฅ samt plinga klockan samt plinga boxen"
            "sรคg ELD ELD ELD ELD samt vรคnta femton sekunder sedan sรคg ELD ELD ELD ELD!"
          ];
        };

        # ๐Ÿฆ† says โฎž 4. dimmer actions automations
        dimmer_actions = {
          bedroom      = {
            off_hold_release   = {
              enable           = true;
              description      = "Turn off all configured light devices and turn of kitchen fan";
              extra_actions    = [];
              override_actions = [ "slรคck alla lampor sedan stรคng av flรคkten" ];
            };
          };

          kitchen = {
            on_press_release = {
              enable         = true;
              description    = "Turn on kitchen fan (+ lights)";
              extra_actions  = [ "starta flรคkten" ];
              override_actions = [ ];
            };
          };

          livingroom = {
            on_press_release   = {
              enable           = true;
              description      = "Turn on kitchen fan (+ lights).";
              extra_actions    = [ "starta flรคkten" ];
            };
          };
        };

        # ๐Ÿฆ† says โฎž 5. time based automations
        time_based = {
          morning_wakeup = {
            enable       = true;
            description  = "set morning wakeup alarm (dont miss lunch)";
            conditions  = [ { type = "someone_home"; } ];
            # ๐Ÿฆ† says โฎž create da alarm at 01 AM mon-fri
            schedule     = {
              start      = "01:00";
              days       = ["mon" "tue" "wed" "thu" "fri"];
            }; #  ๐Ÿฆ†  โฎž but we set da alarm to:
            # ๐Ÿฆ† says โฎž 11.30 AM (i like to sleep in)
            actions      = [ "vรคck mig klockan elva och trettio'" ];
          };

          cacel_weekend  = {
            enable       = true;
            description  = "No wakeup on weekends";
            schedule     = { start = "04:00"; days = ["sat" "sun"]; };
            actions      = [ "vรคck mig inte klockan elva och trettio'" ];
          };

          # ๐Ÿฆ† says โฎž daily tv scraper
          tv_scraping   = {
            enable      = true;
            description = "Scrape TV-guide";
            # ๐Ÿฆ† says โฎž 03:30 AM everyday
            schedule    = {
              start     = "03:30";
              days      = ["mon" "tue" "wed" "thu" "fri" "sat" "sun"];
            };
            # ๐Ÿฆ† says โฎž scrape & save so frontend can read it (from zigduck user)
            actions  = [ "skrapa webben efter tv guiden och spara i ${zigduck.stateDir}" ];
          };

          battery_check = {
            enable      = true;
            description = "Check battery driven devices and send notification if any is really low.";
            schedule    = {
              start     = "12:30";
              days      = ["mon" "wed" "sat" ];
            }; # ๐Ÿฆ† says โฎž if any battery driven device has less battery than six send a notification
            actions  = [ "om nรฅgon batteridriven enhet har mindre batteri รคn sex sรฅ skicka en notis om ๐Ÿชซ BATTERY!!" ];
          };
        };

        # ๐Ÿฆ† says โฎž 6. presence based automations
        presence_based = {};
      };


  # ๐Ÿฆ† โฎž SCENES โฎœ
      scenes = {
          # ๐Ÿฆ† says โฎž Scene name
          "Chill Scene"         = {
              # ๐Ÿฆ† says โฎž alternative wordz for da scene
              _aliases          = [ "chill" ];
              # ๐Ÿฆ† says โฎž device friendly name & state
              "PC"              = { state = "ON"; brightness = 200; color = { hex = "#8A2BE2"; }; };
              "Golvet"          = { state = "ON"; brightness = 200; color = { hex = "#40E0D0"; }; };
              "Uppe"            = { state = "ON"; brightness = 200; color = { hex = "#FF69B4"; }; };
              "Spotlight kรถk 1" = { state = "OFF"; brightness = 200; color = { hex = "#FFD700"; }; };
              "Spotlight Kรถk 2" = { state = "OFF"; brightness = 200; color = { hex = "#FF8C00"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 200; color = { hex = "#00CED1"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 200; color = { hex = "#9932CC"; }; };
              "Bloom"           = { state = "ON"; brightness = 200; color = { hex = "#FFB6C1"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; };
          };

          "Green D"             = {
              _aliases        = [ "devil" "devils" "lรถven" ];
              "Golvet"          = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Uppe"            = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Spotlight kรถk 1" = { state = "OFF"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Spotlight Kรถk 2" = { state = "OFF"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Bloom"           = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
          };

          "kitchenInstant"      = {
              _aliases          = [ "instant" "direkt" ];
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#7852A9"; }; };
              "Kรถk Bรคnk Slinga" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Spotlight Kรถk 2" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Spotlight kรถk 1" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
          };

          "kitchenGreenInstant" = {
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#00FF00"; }; };
              "Kรถk Bรคnk Slinga" = { state = "ON"; brightness = 254; color = { hex = "#00FF00"; }; };
              "Spotlight Kรถk 2" = { state = "ON"; brightness = 254; color = { hex = "#00FF00"; }; };
              "Spotlight kรถk 1" = { state = "ON"; brightness = 254; color = { hex = "#00FF00"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
          };

          # ๐Ÿฆ† says โฎž veeeery slow turn off kitchen
          "kitchenFadeOff"      = {
              _aliases          = [ "fade" "tona" ];
              "Golvet"          = { state = "OFF"; transition = 180; };
              "Kรถk Bรคnk Slinga" = { state = "OFF"; transition = 180; };
              "PC"              = { state = "OFF"; transition = 180; };
              "Spotlight Kรถk 2" = { state = "OFF"; transition = 180; };
              "Spotlight kรถk 1" = { state = "OFF"; transition = 180; };
              "Uppe"            = { state = "OFF"; transition = 180; };
          };

          "dark"                = { # ๐Ÿฆ† says โฎž eat darkness... lol YO! You're as blind as me now! HA HA!
              "Bloom"           = { state = "OFF"; transition = 13; };
              "Dรถrrslinga"      = { state = "OFF"; transition = 13; };
              "Golvet"          = { state = "OFF"; transition = 13; };
              "Kรถk Bรคnk Slinga" = { state = "OFF"; transition = 13; };
              "PC"              = { state = "OFF"; transition = 13; };
              "Rustning"        = { state = "OFF"; transition = 13; };
              "Spotlight Kรถk 2" = { state = "OFF"; transition = 13; };
              "Spotlight kรถk 1" = { state = "OFF"; transition = 13; };
              "Sรคnggavel"       = { state = "OFF"; transition = 13; };
              "Tak Hall"        = { state = "OFF"; transition = 13; };
              "Taket Sovrum 1"  = { state = "OFF"; transition = 13; };
              "Taket Sovrum 2"  = { state = "OFF"; transition = 13; };
              "Uppe"            = { state = "OFF"; transition = 13; };
              "Vรคgg"            = { state = "OFF"; transition = 13; };
              "WC 1"            = { state = "OFF"; transition = 13; };
              "WC 2"            = { state = "OFF"; transition = 13; };
              "Takkrona 1"      = { state = "OFF"; transition = 13; };
              "Takkrona 2"      = { state = "OFF"; transition = 13; };
              "Takkrona 3"      = { state = "OFF"; transition = 13; };
              "Takkrona 4"      = { state = "OFF"; transition = 13; };
              "TV Play Strip"   = { state = "OFF"; transition = 13; };
              "TV Play 1"       = { state = "OFF"; transition = 13; };
              "TV Play 2"       = { state = "OFF"; transition = 13; };
              "TV Play 3"       = { state = "OFF"; transition = 13; };
              "TV Play 4"       = { state = "OFF"; transition = 13; };
              "Play Top L"      = { state = "OFF"; transition = 13; };
              "Play Top R"      = { state = "OFF"; transition = 13; };
          };

          "dark-fast"           = { # ๐Ÿฆ† says โฎž eat darkness... NAO!
              "Bloom"           = { state = "OFF"; };
              "Dรถrrslinga"      = { state = "OFF"; };
              "Golvet"          = { state = "OFF"; };
              "Kรถk Bรคnk Slinga" = { state = "OFF"; };
              "PC"              = { state = "OFF"; };
              "Rustning"        = { state = "OFF"; };
              "Spotlight Kรถk 2" = { state = "OFF"; };
              "Spotlight kรถk 1" = { state = "OFF"; };
              "Sรคnggavel"       = { state = "OFF"; };
              "Tak Hall"        = { state = "OFF"; };
              "Taket Sovrum 1"  = { state = "OFF"; };
              "Taket Sovrum 2"  = { state = "OFF"; };
              "Uppe"            = { state = "OFF"; };
              "Vรคgg"            = { state = "OFF"; };
              "WC 1"            = { state = "OFF"; };
              "WC 2"            = { state = "OFF"; };
              "Takkrona 1"      = { state = "OFF"; };
              "Takkrona 2"      = { state = "OFF"; };
              "Takkrona 3"      = { state = "OFF"; };
              "Takkrona 4"      = { state = "OFF"; };
              "TV Play Strip"   = { state = "OFF"; };
              "TV Play 1"       = { state = "OFF"; };
              "TV Play 2"       = { state = "OFF"; };
              "TV Play 3"       = { state = "OFF"; };
              "TV Play 4"       = { state = "OFF"; };
              "Play Top L"      = { state = "OFF"; };
              "Play Top R"      = { state = "OFF"; };
          };

          "max"                 = { # ๐Ÿฆ† says โฎž let there be light
              _aliases          = [ "maxa" ];
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#7852A9"; }; };
              "Kรถk Bรคnk Slinga" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "PC"              = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Rustning"        = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Spotlight Kรถk 2" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Spotlight kรถk 1" = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Tak Hall"        = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Vรคgg"            = { state = "ON"; brightness = 1; }; # ๐Ÿฆ† says โฎž 1% bright enuff on dis qwackit...
              "WC 1"            = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "WC 2"            = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 254; color = { hex = "#FFFFFF"; }; };
              "TV Play Strip"   = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "TV Play 1"       = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "TV Play 2"       = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "TV Play 3"       = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "TV Play 4"       = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "Play Top L"      = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
              "Play Top R"      = { state = "ON"; brightness = 254; color = { xy = [ 0.3127 0.3290 ]; }; };
          };

          "Living Cealing 1"    = { # ๐Ÿฆ† says โฎž makin' living scene dat eazy ?!
              _aliases        = [ "ett" ];
              "Takkrona 1"      = { state = "ON"; brightness = 240; transition = 12; color = { hex = "#FF1744"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 220; transition = 17; color = { hex = "#FFEA00"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 200; transition = 22; color = { hex = "#00E676"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 180; transition = 27; color = { hex = "#2979FF"; }; };
          };
          "Living Cealing 2"    = { # ๐Ÿฆ†โฎžcall "Living Cealing 1", sleep 15, "Living Cealing 2", sleep 15. "Living Cealing 3"
              _aliases        = [ "tvรฅ" ];
              "Takkrona 1"      = { state = "ON"; brightness = 182; transition = 9; color = { hex = "#2979FF"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 200; transition = 11; color = { hex = "#00E676"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 220; transition = 18; color = { hex = "#FFEA00"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 240; transition = 26; color = { hex = "#FF1744"; }; };
          };
          "Living Cealing 3"    = {
              _aliases        = [ "tre" ];
              "Takkrona 1"      = { state = "ON"; brightness = 200; transition =  8; color = { hex = "#B71C1C"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 220; transition = 14; color = { hex = "#1B5E20"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 240; transition = 20; color = { hex = "#0D47A1"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 200; transition = 26; color = { hex = "#4A148C"; }; };
          };

          "Rainforest"          = {
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FF1744"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 180; color = { hex = "#2E7D32"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#1B5E20"; }; };
              "PC"              = { state = "ON"; brightness = 220; color = { hex = "#00ACC1"; }; };
              "Rustning"        = { state = "ON"; brightness = 254; color = { hex = "#FF6D00"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 254; color = { hex = "#FF4081"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 160; color = { hex = "#2E7D32"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 120; color = { hex = "#1B5E20"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFD54F"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 220; color = { hex = "#81C784"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 200; color = { hex = "#66BB6A"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 180; color = { hex = "#43A047"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 160; color = { hex = "#2E7D32"; }; };
          };

          "Galaxy"              = {
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FFD54F"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 140; color = { hex = "#4A148C"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#12002B"; }; };
              "PC"              = { state = "ON"; brightness = 200; color = { hex = "#7B1FA2"; }; };
              "Rustning"        = { state = "ON"; brightness = 120; color = { hex = "#0D47A1"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 220; color = { hex = "#FF80AB"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 120; color = { hex = "#4A148C"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 100; color = { hex = "#311B92"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#E1BEE7"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 200; color = { hex = "#7B1FA2"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 180; color = { hex = "#6A1B9A"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 160; color = { hex = "#4A148C"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 140; color = { hex = "#311B92"; }; };
          };

          "Festival"            = {
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FF1744"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 254; color = { hex = "#FF9100"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#FFEA00"; }; };
              "PC"              = { state = "ON"; brightness = 254; color = { hex = "#00E676"; }; };
              "Rustning"        = { state = "ON"; brightness = 254; color = { hex = "#00E5FF"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 254; color = { hex = "#D500F9"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 220; color = { hex = "#FF1744"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 220; color = { hex = "#FF9100"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFEA00"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 254; color = { hex = "#651FFF"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 254; color = { hex = "#D500F9"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 254; color = { hex = "#F50057"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 254; color = { hex = "#FF1744"; }; };
          };

          "Sunset"              = {
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FF3D00"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 200; color = { hex = "#FF6E40"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#4E342E"; }; };
              "PC"              = { state = "ON"; brightness = 240; color = { hex = "#FFAB40"; }; };
              "Rustning"        = { state = "ON"; brightness = 254; color = { hex = "#FFC107"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 254; color = { hex = "#FF4081"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 160; color = { hex = "#7E57C2"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 120; color = { hex = "#3F51B5"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#FFD180"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 240; color = { hex = "#FF8A80"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 220; color = { hex = "#FF80AB"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 200; color = { hex = "#CE93D8"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 180; color = { hex = "#9FA8DA"; }; };
          };

          "Underwater"          = {
              "Bloom"           = { state = "ON"; brightness = 254; color = { hex = "#FF6E40"; }; };
              "Dรถrrslinga"      = { state = "ON"; brightness = 160; color = { hex = "#006064"; }; };
              "Golvet"          = { state = "ON"; brightness = 124; color = { hex = "#002F4A"; }; };
              "PC"              = { state = "ON"; brightness = 220; color = { hex = "#0091EA"; }; };
              "Rustning"        = { state = "ON"; brightness = 254; color = { hex = "#18FFFF"; }; };
              "Sรคnggavel"       = { state = "ON"; brightness = 240; color = { hex = "#64FFDA"; }; };
              "Taket Sovrum 1"  = { state = "ON"; brightness = 140; color = { hex = "#01579B"; }; };
              "Taket Sovrum 2"  = { state = "ON"; brightness = 110; color = { hex = "#003366"; }; };
              "Uppe"            = { state = "ON"; brightness = 254; color = { hex = "#84FFFF"; }; };
              "Takkrona 1"      = { state = "ON"; brightness = 220; color = { hex = "#00ACC1"; }; };
              "Takkrona 2"      = { state = "ON"; brightness = 200; color = { hex = "#00838F"; }; };
              "Takkrona 3"      = { state = "ON"; brightness = 180; color = { hex = "#00695C"; }; };
              "Takkrona 4"      = { state = "ON"; brightness = 160; color = { hex = "#26A69A"; }; };
          };

          "tv-area1"          = {
              _aliases        = [ "tv1" "tv area 1" "tv area ett" ];
              "TV Play Strip" = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.6321 0.2678 ]; }; transition = 15; };
              "TV Play 1"     = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.1491 0.3012 ]; }; transition = 15; };
              "TV Play 2"     = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.2654 0.6680 ]; }; transition = 15; };
              "TV Play 3"     = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.4995 0.4697 ]; }; transition = 15; };
              "TV Play 4"     = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.2293 0.0945 ]; }; transition = 15; };
              "Play Top L"    = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.6187 0.3687 ]; }; transition = 15; };
              "Play Top R"    = { state = "ON"; brightness = 254; hue = 49460; sat = 242; color = { xy = [ 0.1611 0.5294 ]; }; transition = 15; };
          };

          "tv-area2"          = {
              _aliases        = [ "tv2" "tv area 2" "tv area tvรฅ" ];
              "TV Play Strip" = { state = "ON"; brightness = 254; hue = 56100; sat = 250; color = { xy = [ 0.3824 0.1600 ]; }; transition = 15; };
              "TV Play 1"     = { state = "ON"; brightness = 240; hue = 56100; sat = 250; color = { xy = [ 0.1682 0.0410 ]; }; transition = 15; };
              "TV Play 2"     = { state = "ON"; brightness = 240; hue = 56100; sat = 250; color = { xy = [ 0.1532 0.0475 ]; }; transition = 15; };
              "TV Play 3"     = { state = "ON"; brightness = 240; hue = 56100; sat = 250; color = { xy = [ 0.2746 0.1320 ]; }; transition = 15; };
              "TV Play 4"     = { state = "ON"; brightness = 240; hue = 56100; sat = 250; color = { xy = [ 0.4088 0.5170 ]; }; transition = 15; };
              "Play Top L"    = { state = "ON"; brightness = 254; hue = 56100; sat = 250; color = { xy = [ 0.2255 0.3299 ]; }; transition = 15; };
              "Play Top R"    = { state = "ON"; brightness = 254; hue = 56100; sat = 250; color = { xy = [ 0.1670 0.3520 ]; }; transition = 15; };
          };

          "tv-area3"          = {
              _aliases        = [ "tv3" "tv area 3" "tv area tre" ];
              "TV Play Strip" = { state = "ON"; brightness = 254; color = { xy = [ 0.7006 0.2993 ]; }; transition =  2; };
              "TV Play 1"     = { state = "ON"; brightness = 210; color = { xy = [ 0.5530 0.4100 ]; }; transition =  4; };
              "TV Play 2"     = { state = "ON"; brightness = 254; color = { xy = [ 0.4432 0.5151 ]; }; transition =  6; };
              "TV Play 3"     = { state = "ON"; brightness = 200; color = { xy = [ 0.1700 0.7000 ]; }; transition =  8; };
              "TV Play 4"     = { state = "ON"; brightness = 254; color = { xy = [ 0.1700 0.3400 ]; }; transition = 10; };
              "Play Top L"    = { state = "ON"; brightness = 220; color = { xy = [ 0.1350 0.0400 ]; }; transition = 12; };
              "Play Top R"    = { state = "ON"; brightness = 254; color = { xy = [ 0.2654 0.1080 ]; }; transition = 14; };
          };
        };
    };

    # ๐Ÿฆ† โฎž TV โฎœ
    # ๐Ÿฆ†saysโฎž configure TV devices with: room, ip, apps & channel information
    tv       = {
  
      # ๐Ÿฆ† says โฎž Livingroom
      shield = {
        #privateKeyFile = config.sops.secrets.adb.path;
        #publicKey      = "QAAAACEJNfsfRV4PQ9Ah87MbTVbMkbXC6CAMDOR+0K6mIpv/4TSzYMkc2qit3Kryc55IVOjwR3fJRjj/uL549gZ7nEemWtcd3AsYQBp0iIEor8nu1L/V6jfsTY6Xe/pl06xoroy6OwZRWuDbZ4wD2xQRRQjfPd+JtYnMAWneM6r1V15uR67w4ITvjk3ckyfgNeLZMUwahMRjC3wSjaU9sAdKNmg8yPd8uHZ+mK6mstxJFAGEpnnm1lE7Z2r0DF6h6MKY1++dwhU+WM5BRDNiBg+D4i6fDW4+Z1I9ENuFnjT17zAxZXch04SNlG3O94BANYP7jmKp60OvtDL6msfphntuIUzMCkndF9De0Kv4lJdQxe1d+wf+AFpmtd/xtrk45YdMV+eWCJf2OkidaHmSj4ffkAobpun0VrkZN2Z1JymmdsvUbyMjAsby3Zun0xr3EocUS8Jy5TcsK/dcpD6CB5dqzlHhsHSAWt2TDwPzZYXgV1xc+q+PqM09OVN1xActJu75UMkg5b84U15hwQvYdwB8UaopMWWk6p064c7gxYSfH7fSxwkW2Jy1CElgJa55Pp4SZG9b/3B+VcNL1WSf6v/lvJqPbrRvBqvS0+e9wcFMNZtQKTX3n5X0wW1/czZPCQX+hmM8Uu1qrtaz4rKViIEGf4YR0/9eUGYQVfuAxAh8ZmsroJlnAAEAAQA= pungkula@desktop";
          
        enable       = true;
        room         = "tv-area";
        ip           = devices.shield.ip;
        apps = {
          telenor    = "se.telenor.stream/.MainActivity";
          tv4        = "se.tv4.tv4playtab/se.tv4.tv4play.ui.mobile.main.BottomNavigationActivity";
        };

        # ๐Ÿฆ† says โฎž EPG scraper/builder (can be overridden per-channel too)
        scraper      = {
          row_xpath                = ''//table[@id="channel-schedule"]//tr'';
          time_xpath               = ".//time";
          time_attr                = "datetime";
          title_xpath              = ''.//a[contains(@class, "program-title")]'';
          desc_xpath               = ".//p";
          time_formats             = [ "%H:%M" "%H.%M" ];
          time_strip_pattern       = ''[^\d:\.]'';
          default_duration_minutes = 30;
          timezone_offset_hours    = timezone_offset_hours;
          pm_heuristic_after_hour  = null;
        };

        # ๐Ÿฆ† says โฎž channels on diz TV!
        channels       = {
          "1"          = {
            name       = "SVT1";
            id         = 1; # ๐Ÿฆ† says โฎž adb channel ID
            # ๐Ÿฆ† says โฎž OR
            # stream_url = "https://url.com/";
            # ๐Ÿฆ† โฎž commandz to start this channel!
            cmd        = "open_telenor && wait 5 && start_channel_1";
            # ๐Ÿฆ† says โฎž automagi generated tv-guide web & EPG
            icon       = ./themes/icons/tv/1.png;
            scrape_url = "https://tv-tabla.se/tabla/svt1/";
            # ๐Ÿฆ† says โฎž tv-scraper PER-CHANNEL overrides
            # scraper  = {};
          };
          "2"          = {
            id         = 2;
            name       = "SVT2";
            cmd        = "open_telenor && wait 5 && start_channel_2";
            icon       = ./themes/icons/tv/2.png;
            scrape_url = "https://tv-tabla.se/tabla/svt2/";
          };
          "3"          = {
            id         = 3;
            name       = "Kanal 3";
            cmd        = "open_telenor && wait 5 && start_channel_3";
            icon       = ./themes/icons/tv/3.png;
            scrape_url = "https://tv-tabla.se/tabla/tv3/";
          };
          "4"          = {
            id         = 4;
            name       = "TV4";
            cmd        = "open_telenor && wait 5 && start_channel_4";
            icon       = ./themes/icons/tv/4.png;
            scrape_url = "https://tv-tabla.se/tabla/tv4/";
          };
          "5"          = {
            id         = 5;
            name       = "Kanal 5";
            cmd        = "open_telenor && wait 5 && start_channel_5";
            icon       = ./themes/icons/tv/5.png;
            scrape_url = "https://tv-tabla.se/tabla/kanal_5/";
          };
          "6"          = {
            id         = 6;
            name       = "Kanal 6";
            cmd        = "open_telenor && wait 5 && start_channel_6";
            icon       = ./themes/icons/tv/6.png;
            scrape_url = "https://tv-tabla.se/tabla/tv6/";
          };
          "7"          = {
            id         = 7;
            name       = "Sjuan";
            cmd        = "open_telenor && wait 5 && start_channel_7";
            icon       = ./themes/icons/tv/7.png;
            scrape_url = "https://tv-tabla.se/tabla/sjuan/";
          };
          "8"          = {
            id         = 8;
            name       = "TV8";
            icon       = ./themes/icons/tv/8.png;
            scrape_url = "https://tv-tabla.se/tabla/tv8/";
          };
          "9"          = {
            id         = 9;
            name       = "Kanal 9";
            icon       = ./themes/icons/tv/9.png;
            scrape_url = "https://tv-tabla.se/tabla/kanal_9/";
          };
          "10"         = {
            id         = 10;
            name       = "Kanal 10";
            icon       = ./themes/icons/tv/10.png;
            scrape_url = "https://tv-tabla.se/tabla/tv10/";
          };
          "11"         = {
            id         = 11;
            name       = "Kanal 11";
            icon       = ./themes/icons/tv/11.png;
            scrape_url = "https://tv-tabla.se/tabla/tv11/";
          };
          "12"         = {
            id         = 12;
            name       = "Kanal 12";
            icon       = ./themes/icons/tv/12.png;
            scrape_url = "https://tv-tabla.se/tabla/tv12/";
          };
          "13"         = {
            id         = 13;
            name       = "TV4 Hockey";
            icon       = ./themes/icons/tv/13.png;
            cmd        = "open_tv4 && nav_select && nav_left && nav_down && nav_down && nav_down && nav_select && wait 3 && nav_down && nav_down && nav_down && nav_down && nav_down && nav_select";
            scrape_url = "https://tv-tabla.se/tabla/tv4_hockey/";
          };
          "14"         = {
            id         = 14;
            name       = "TV4 Sport Live 1";
            icon       = ./themes/icons/tv/14.png;
            cmd        = "open_tv4 && nav_left && nav_down && nav_down && nav_down && nav_select && wait 3 && nav_down && nav_down && nav_down && nav_down && nav_down && nav_right && nav_right && nav_select";
            scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_1/";
          };
          "15"         = {
            id         = 15;
            name       = "TV4 Sport Live 2";
            icon       = ./themes/icons/tv/15.png;
            cmd        = "open_tv4 && nav_select && nav_left && nav_down && nav_down && nav_down && nav_select && wait 3 && nav_down && nav_down && nav_down && nav_down && nav_down && nav_down && nav_select";
            scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_2/";
          };
          "16"         = {
            id         = 16;
            name       = "TV4 Sport Live 3";
            icon       = ./themes/icons/tv/16.png;
            cmd        = "open_tv4 && nav_down && nav_right && nav_right && nav_center";
            scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_3/";
          };
          "17"         = {
            id         = 17;
            name       = "TV4 Sport Live 4";
            icon       = ./themes/icons/tv/17.png;
            cmd        = "open_tv4 && nav_left && nav_down && nav_down && nav_down && nav_select && wait 3 && nav_down && nav_down && nav_down && nav_down && nav_down && nav_down && nav_right && nav_right && nav_select";
            scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_4/";
          };
        };
      };
    };


# ๐Ÿฆ† says โฎž DASHBOARD CONFIOGURATION
    https.dashboard.urlFile = config.sops.secrets.dashboard.path;

    dashboard   = {
      # ๐Ÿฆ† says โฎž  home page information cards
      statusCards          = {
        calendar           = {
          enable           = true;
          title            = "๐‘ช๐‘จ๐‘ณ๐‘ฌ๐‘ต๐‘ซ๐‘จ๐‘น";
          group            = "1";
          icon             = "fas fa-calendar";
          color            = "#ff0000";
          theme            = "glass";
          filePath         = "${zigduck.stateDir}/calendar.json";
          jsonField        = "today_date";
          format           = "{value}";
          detailsJsonField = "today_events";
          detailsFormat    = "{value}";
          chart            = false;
        };

        # ๐Ÿฆ† says โฎž Monero USD price ticker
        xmr                = {
          enable           = true;
          title            = "๐‘ฟ๐‘ด๐‘น";
          group            = "tickers";
          icon             = "fab fa-monero";
          color            = "#a78bfa";
          theme            = "colorful";
          filePath         = "${zigduck.stateDir}/xmr.json";
          jsonField        = "current_price";
          format           = "{value}";
          detailsJsonField = "7d_change";
          detailsFormat    = "7d: {value}%";
          chart            = true;
          historyField     = "history";
        };

        # ๐Ÿฆ† says โฎž Bitcoin USD price ticker
        btc                = {
          enable           = true;
          title            = "๐‘ฉ๐‘ป๐‘ช";
          group            = "tickers";
          icon             = "fab fa-bitcoin";
          color            = "#ff6600";
          filePath         = "${zigduck.stateDir}/btc.json";
          jsonField        = "current_price";
          format           = "{value}";
          detailsJsonField = "7d_change";
          detailsFormat    = "7d: {value}%";
          chart            = true;
          historyField     = "history";
        };

        # ๐Ÿฆ† says โฎž kWh/price chart card
        energyPrice    = {
          enable       = true;
          title        = "๐‘ท๐‘น๐‘ฐ๐‘ช๐‘ฌ";
          group        = "energy";
          icon         = "fas fa-bolt";
          color        = "#ffff00";
          filePath     = "${zigduck.stateDir}/energy_price.json";
          jsonField    = "current_price";
          format       = "{value} SEK/kWh";
          chart        = true;
          historyField = "history";
        };

        # ๐Ÿฆ† says โฎž energy usage card
        energyUsage    = {
          enable       = true;
          title        = "USAGE";
          group        = "energy";
          icon         = "fas fa-bolt";
          color        = "#ffff00";
          filePath     = "${zigduck.stateDir}/energy_usage.json";
          jsonField    = "monthly_usage";
          format       = "{value} kWh";
          chart        = true;
          historyField = "history";
        };

        # ๐Ÿฆ† says โฎž show indoor temperature
        temperature     = {
          enable        = true;
          title         = "TEMPERATURE C";
          group         = "sensors";
          icon          = "fas fa-thermometer-half";
          color         = "#e74c3c";
          theme         = "glass";
          filePath      = "${zigduck.stateDir}/temperature.json";
          jsonField     = "temperature";
          format        = "{value} ยฐC";
          detailsFormat = "Temperature in Hallway";
          chart         = true;
          historyField  = "history";
        };
      };

      # ๐Ÿฆ† says โฎž DASHBOARD PAGES (extra tabs)
      pages  = {
        # ๐Ÿฆ†saysโฎž ChatBot (no LLM) - Less thinkin', more doin'!
        "4"     = {
          icon  = "fas fa-comments";
          title = "chat";
          css   = css.chat;
          # ๐Ÿฆ† says โฎž symlink TTS audio to frontend webserver
          files = { tts = "${zigduck.stateDir}/tts"; };
          code  = pages.chat;
        };
      };
    };
  };

  systemd.tmpfiles.rules = lib.mkIf (lib.elem "zigduck" config.this.host.modules.services) [
    "d  ${zigduck.stateDir}/.config/yo             0700 zigduck zigduck - -"
    "C  ${zigduck.stateDir}/.config/yo/clients.json 0600 zigduck zigduck - ${yoClientsJson}"
  ];

  users.users.zigduck = { extraGroups = [ "zigbee2mqtt" ]; };

  # ๐Ÿฆ† says โฎž hush little duckie don't say a word....
  sops = {
    secrets =  {
      api = {
        sopsFile = ./../secrets/api.yaml;
        owner    = "zigduck";
        group    = "zigduck";
        mode     = "0440"; # Read-only for owner and group
      };
      hueBridgeAPI = {
        sopsFile   = ./../secrets/hueBridgeAPI.yaml;
        owner      = "zigduck";
        group      = "zigduck";
        mode       = "0440"; # Read-only for owner and group
      };
      hueSyncAPI = {
        sopsFile = ./../secrets/hueSyncAPI.yaml;
        owner    = "zigduck";
        group    = "zigduck";
        mode     = "0440"; # Read-only for owner and group
      };
      mosquitto  = { # ๐Ÿฆ† says โฎž quack, stupid!
        sopsFile = ./../secrets/mosquitto.yaml;
        owner    = config.this.user.me.name;
        group    = config.this.user.me.name;
        mode     = "0444"; # ๐Ÿฆ† says โฎž Read-only for owner and group
      }; # ๐Ÿฆ† says โฎž Z2MQTT encryption key - if changed needs re-pairing devices
      z2m_network_key = {
        sopsFile      = ./../secrets/z2m_network_key.yaml;
        owner         = "zigbee2mqtt";
        group         = "zigbee2mqtt";
        mode          = "0440"; # ๐Ÿฆ† says โฎž Read-only for owner and group
      };
      webserver  = { # ๐Ÿฆ† says โฎž https required
        sopsFile = ./../secrets/webserver.yaml;
        owner    = config.this.user.me.name;
        group    = "zigduck";
        mode     = "0440";
      };
      dashboard  = { # ๐Ÿฆ† says โฎž i likez TLS
        sopsFile = ./../secrets/dashboard.yaml;
        owner    = config.this.user.me.name;
        group    = "zigduck";
        mode     = "0440";
      }; # ๐Ÿฆ† says โฎž required for youtube
      youtube_api_key = {
        sopsFile = ./../secrets/youtube.yaml;
        owner    = config.this.user.me.name;
        group    = "zigduck";
        mode     = "0440";
      };
      z2m_mosquitto = {
        sopsFile = ./../secrets/z2m_mosquitto.yaml;
        owner    = "zigbee2mqtt";
        group    = "zigbee2mqtt";
        mode     = "0440"; # ๐Ÿฆ† says โฎž Read-only for owner and group
      };
    };
  # ๐Ÿฆ† says โฎž BYEEEEEEEEE
  };}
I like my flakes tiny & ny modules dynamically loaded,
# dotfiles/flake.nix โฎž https://github.com/quackhack-mcblindy/dotfiles
# ยป โ˜… QuackHack-McBLindy.com โ˜… ยซ
{
    description = "โ„๏ธ๐Ÿฆ† โฎž QuackHack-McBLindy's NixOS flake";
    inputs = {
        nixpkgs.url                     = "github:nixos/nixpkgs/nixos-unstable";
        sops-nix.url                    = "github:Mic92/sops-nix";
        sops-nix.inputs.nixpkgs.follows = "nixpkgs";
        ducktrace-python.url = "github:QuackHack-McBlindy/ducktrace-python";
        yo.url               = "github:QuackHack-McBlindy/yo?ref=dev";
        zigduck.url          = "github:QuackHack-McBlindy/zigduck?ref=dev";
        pokenix.url          = "github:QuackHack-McBlindy/pokenix";
        magnixify.url        = "github:QuackHack-McBlindy/magnixify-gnome-shell-extension";
        caddy-duckdns.url    = "github:QuackHack-McBlindy/nix-caddy-duckdns";
        installer.url        = "github:QuackHack-McBlindy/auto-installer-nixos";
        #yo.url              = "path:/home/pungkula/new/yo";
        #zigduck.url          = "path:/home/pungkula/zigduck2mqttnix";
        #pokenix.url         = "path:/home/pungkula/new/pokenix";
        #magnixify.url       = "path:/home/pungkula/new/magnixify-gnome-shell-extension";
        # ๐Ÿฆ† โฎž mobile specific inputs
        mobile-pkgs.url      = "nixpkgs/dfd82985c273aac6eced03625f454b334daae2e8";
        mobile-nixos = {
          url        = "github:nixos/mobile-nixos/efbe2c3c5409c868309ae0770852638e623690b5";
          flake      = false;
        };
    };
    outputs = inputs @ { self, systems, nixpkgs, mobile-pkgs, mobile-nixos, ... }:
        let
            lib = import ./lib {
                inherit self inputs;
                lib = nixpkgs.lib;
            };
        in lib.makeFlake {
            systems     = [ "x86_64-linux" "aarch64-linux" ];
            overlays    = lib.mapOverlays ./overlays { inherit lib self inputs; };
            hosts       = lib.mapHosts ./hosts;
            packages    = lib.mapModules ./packages import;
            devShells   = lib.mapModules ./devShells (path: import path);
            specialArgs = { pkgs = system: nixpkgs.legacyPackages.${system}; };
        };} # ๐Ÿฆ† duck say โฎž flakes all set, with no debating โ€” next nix file awaiting, ducks be there waitin'

View Flake Outputs
git+file:///home/pungkula/dotfiles
โ”œโ”€โ”€โ”€devShells
โ”‚   โ”œโ”€โ”€โ”€aarch64-linux
โ”‚   โ”‚   โ”œโ”€โ”€โ”€android omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€default omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€esp32-rs omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€esphome omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€go omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€java omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€node omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€python312 omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€python38 omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ”œโ”€โ”€โ”€rust omitted (use '--all-systems' to show)
โ”‚   โ”‚   โ””โ”€โ”€โ”€simple omitted (use '--all-systems' to show)
โ”‚   โ””โ”€โ”€โ”€x86_64-linux
โ”‚       โ”œโ”€โ”€โ”€android: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€default: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€esp32-rs: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€esphome: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€go: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€java: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€node: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€python312: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€python38: development environment 'nix-shell'
โ”‚       โ”œโ”€โ”€โ”€rust: development environment 'nix-shell'
โ”‚       โ””โ”€โ”€โ”€simple: development environment 'nix-shell'
โ”œโ”€โ”€โ”€nixosConfigurations
โ”‚   โ”œโ”€โ”€โ”€desktop: NixOS configuration
โ”‚   โ”œโ”€โ”€โ”€homie: NixOS configuration
โ”‚   โ”œโ”€โ”€โ”€laptop: NixOS configuration
โ”‚   โ”œโ”€โ”€โ”€nasty: NixOS configuration
โ”‚   โ””โ”€โ”€โ”€pinephone: NixOS configuration
โ”œโ”€โ”€โ”€overlays
โ”‚   โ”œโ”€โ”€โ”€noisereduce: Nixpkgs overlay
โ”‚   โ””โ”€โ”€โ”€quackhack-mcpkgs: Nixpkgs overlay
โ””โ”€โ”€โ”€packages
    โ”œโ”€โ”€โ”€aarch64-linux
    โ”‚   โ”œโ”€โ”€โ”€health-rs omitted (use '--all-systems' to show)
    โ”‚   โ””โ”€โ”€โ”€kagi omitted (use '--all-systems' to show)
    โ””โ”€โ”€โ”€x86_64-linux
        โ”œโ”€โ”€โ”€health-rs: package 'health-rs-0.1.0'
        โ”œโ”€โ”€โ”€installer: package 'nixos-auto-installer-24.05.20240406.ff0dbd9-x86_64-linux.iso'
        โ””โ”€โ”€โ”€kagi: package 'kagi'

๐Ÿ›Ÿ Quick Start

Build automated, offline USB NixOS installer

$ sudo bash usb-installer \
  --user "pungkula" \
  --host "laptop" \
  --ssid "IfYouDontHaveEthernet" \
  --wifipass "CanBeOmitted" \
  --publickey "ssh-ed25519 AAAAC3FoRSsHCoNnEcTiOn..."

# dd result to flash drive (replace sdX)
$ sudo dd if="$(readlink -f ./result/iso/*.iso)" of=/dev/sdX bs=4M status=progress oflag=sync

Plug in flash drive into laptop and boot. Let it work and wait until it powers down. Remove flash drive, boot it up again and deploy configuration from your main machine:

# ๐Ÿฆ†๐Ÿ”“ First deploy? Get your Yubikey: PIN+Touch unlocks host specific AGE key for sops-nix
$ yo deploy laptop

Any builds after first deployment will use local cached binaries for enhanced build time.


๐Ÿš€ yo CLI ๐Ÿฆ†

The `yo` CLI is a framework designed to execute scripts defined in the `./bin` directory. It provides a unified interface for script execution, centralizes all help commands, and automatically validates parametrs and updates the documentation.

Usage: `yo [arguments]`

Usage Examples:

The yo CLI supports flexible parameter parsing through two primary mechanisms:

# Named Parameters
$ yo deploy --host pinephone --flake /home/pungkula/dotfiles

# Positional Parameters
$ yo deploy pinephone /home/pungkula/dotfiles

# Scripts can also be executed with natural language text by typing:
$ yo do "is pinephone overheating"
# Natural language voice commands are also supported, say:
"yo bitch reboot the pinephone"

# If the server is not running, it can be manually started with:
$ yo-rs

โœจ Available Commands

Set default values for your parameters to have them marked [optional] Add `?` to any command to run it in DEBUG mode

Command Syntax Aliases Description VoiceReady
๐Ÿ–ฅ๏ธ System Management
yo deploy --host [--flake] [--user] [--repo] [--port] [--test] [--watchdog] [--watchdog-timer] [--all] Build and deploy a NixOS configuration to a remote host. Bootstraps, builds locally, activates remotely, and auto-tags the generation. โœ…
yo dev [--devShell] [--list] Start development enviorment with local flake inputs ๐Ÿ“›
yo dry Build and deploy a NixOS configuration to a remote host. Bootstraps, builds locally, activates remotely, and auto-tags the generation. ๐Ÿ“›
yo pkgs [--host] --pkgs [--file] Add system package to specified hosts nixosConfiguration. ๐Ÿ“›
yo reboot [--host] restart Force reboot and wait for host โœ…
yo rollback --host [--flake] [--user] Rollback a host to a previous NixOS generation. Fetches Git tags and reverts system+config to a synced, tagged state. ๐Ÿ“›
yo services --operation --service --host [--user] [--port] [--!] Systemd service handler. โœ…
yo switch [--flake] [--!] rb Rebuild and switch with dry runs as default ('!' to save new system generation) โœ…
yo watch [--device] --setting [--value] API controller for ESP32-S3-WATCH-rs. โœ…
yo zoom [--action] Magnify the display enough even a blind duck can see a little.. โœ…
โ˜Ž๏ธ Phone
yo call --contactName --contactFile Calls phone number from contact list โœ…
yo text --contactName --contactFile Text message a phone number from contact list โœ…
โšก Productivity
yo calculator --expression calc Calculate math expressions โœ…
yo calendar [--operation] [--calenders] kal Calendar assistant. Provides easy calendar access. Interactive terminal calendar, or manage the calendar through yo commands or with voice. โœ…
yo clip2phone --copy Send clipboard to an iPhone, for quick copy paste โœ…
yo fr --find --replace find and replace in .nix files and subdirs โœ…
yo fzf f Interactive fzf search for file content with quick edit & jump to line ๐Ÿ“›
yo hitta --search Locate a persons address with help of Hitta.se โœ…
yo img2phone --image Send images to an iPhone ๐Ÿ“›
yo pr-release [--confirm] Creates PR dev > main to release, changelog as description. ๐Ÿ“›
yo pull [--flake] [--host] Pull the latest changes from your dotfiles repo. Resets tracked files to origin/main but keeps local extras. โœ…
yo pull-request pr Copies git diff to clipboard for summarizing ๐Ÿ“›
yo push [--flake] [--repo] [--host] [--generation] ps Commit, tag, and push dotfiles and system state to GitHub. Tags based on host + generation, auto-updates README, and preserves history. ๐Ÿ“›
yo release [--confirm] Bumps project version & commit, tag, and push to Git. ๐Ÿ“›
yo scp --host [--path] [--username] [--downloadPath] Move files between hosts interactively ๐Ÿ“›
yo search --search [--token-file] [--num-results] Perform web search using Kagi with Quick Answer โœ…
yo sum-pr Copies git diff to clipboard for summarizing ๐Ÿ“›
yo update-readme [--readmePath] Updates documentation in README.md with current system state (uses build-time config) ๐Ÿ“›
๐ŸŒ Localization
yo stores --store_name [--location] [--radius] store, shop Finds nearby stores using OpenStreetMap data with fuzzy name matching. Returns results with opening hours. โœ…
yo travel [--to] [--from] [--type] [--apikeyPath] Public transportation helper. Fetches current bus, boat, train and air travel schedules. (Sweden) โœ…
yo weather [--location] [--day] [--condition] [--locationPath] weat Weather Assistant. Ask anything weather related (3 day forecast) โœ…
๐ŸŒ Networking
yo block --url [--blocklist] ad Block URLs using DNS ๐Ÿ“›
yo esp-get get-esp Microcontroller control script - enabling full device configuration on the fly with voice commands. ๐Ÿ“›
yo ip-updater [--token1] [--token2] [--token3] DDNS updater โœ…
yo notify-me [--address] [--port] [--dataDir] Notification server for iOS devices ๐Ÿ“›
yo shareWiFi [--ssidFile] [--passwordFile] creates a QR code of guest WiFi and push image to iPhone โœ…
yo speed st Test internet download speed โœ…
๐ŸŽง Media Management
yo call-remote Used to call the tv remote, for easy localization. โœ…
yo hacker-news [--show] [--item] [--user] [--clear] [--number] hn Hacker news API controller ๐Ÿ“›
yo news [--apis] [--clear] API caller and playlist manager for latest Swedish news from SR. โœ…
yo transcode [--directory] trans Transcode media files ๐Ÿ“›
yo tv [--typ] [--search] [--room] [--season] [--shuffle] Android TV Controller. Fuzzy search all media types and creates playlist and serves over webserver for casting. โœ…
yo tv-guide [--search] [--channel] [--jsonFilePath] tvg TV-guide assistant.. โœ…
yo tv-rs [--type] [--search] [--dir] [--build] High performance Media Management written in Rust. ๐Ÿ“›
yo tv-scrape [--dir] Scrape the web for TV-guide data and save data for frontend. โœ…
yo tv-scraper [--epgFilePath] [--jsonFilePath] [--htmlOutPath] [--flake] tvs Scrapes web for tv-listing data. Builds EPG and generates HTML. ๐Ÿ“›
yo vlc [--add] [--addDir] [--remove] [--list] [--shuffle] [--clear] [--playlist] Playlist management for the local machine ๐Ÿ“›
๐Ÿ“ File Operations
yo copy --from --to cp Copy a file or directory to a new location โœ…
yo list [--path] ls List directory contents with details โœ…
yo makedir --path mkd Create a new directory with parents if needed โœ…
yo move --from --to mv Move a file or directory to a new location โœ…
yo nano --file --content Write content to filepath โœ…
yo remove --target rm, delete Remove files or directories safely โœ…
๐Ÿ” Security & Encryption
yo sops --input [--operation] [--value] [--output] [--agePub] e Encrypts a file with sops-nix ๐Ÿ“›
yo yubi --operation --input yk Encrypts and decrypts files using a Yubikey and AGE ๐Ÿ“›
๐Ÿ—ฃ๏ธ Voice
yo do [--input] [--room] do is a natural language to shell script translator that runs exact and fuzzy pattern matching at runtime with automatic parameter resolution and seamless shell script execution ๐Ÿ“›
yo say --text [--model] [--blocking] [--path] [--length-scale] Text to speech with baked in model and full duplex audio streaming ๐Ÿ“›
๐Ÿ›– Home Automation
yo alarm [--hours] [--minutes] [--minuteWord] [--prep] [--list] [--cancel] wakeup Set an alarm for a specified time โœ…
yo bed [--part] [--state] Bed controller โœ…
yo chair [--part] [--state] Chair controller โœ…
yo compare [--device] [--scope] [--metric] [--above] [--below] Return true/false whether a device's numeric metric (power, battery, temperature, humidity, brightness, signal, lux) is above or below a threshold. โœ…
yo countdown --device [--seconds] Set a (nedrรคkning countdown) on a Zigbee device (turns off after N seconds).
yo display --path Creates a HTML image that can be displayed on the chat frontend. โœ…
yo findPhone Helper for locating Phone โœ…
yo flash_lights Flash lights 3 times then restore previous state. โœ…
yo house [--device] [--state] [--brightness] [--color] [--temperature] [--scene] [--room] [--blinds] [--all-lights] [--get-state] High-performance unified CLI for controlling all smart home devices. โœ…
yo if --condition --then [--efter] [--plus] [--annars] Run a command when a condition holds. Condition is any natural-language query that returns true/false. โœ…
yo kitchenFan [--state] Turns kitchen fan on/off โœ…
yo leaving Run when leaving house to set away state ๐Ÿ“›
yo loop [--action] [--name] Handle a configured device loop โ€” repeatedly fires an MQTT topic until the watched device leaves its fingerprint. โœ…
yo mqtt2history --field --file Save an MQTT payload field into a JSON history file for the frontend. โœ…
yo notify [--message] [--tag] Send a desktop notification. โœ…
yo ping [--device] Turns kitchen fan on/off โœ…
yo returned Run when returned home to set home state ๐Ÿ“›
yo robobot --device [--mode] [--state] [--delay] [--reverse] [--lower] [--upper] [--touch] [--user] [--passwordfile] Designed to simplify configuring the Zigbee Fingerbot Plus ๐Ÿ“›
yo scene [--name] Activate a scene by name. โœ…
yo snapshot --action --name Create or restore a named snapshot of the Zigduck light/device states. โœ…
yo state [--device] [--room] [--state] [--reverse] High-performance unified CLI for controlling all smart home devices. โœ…
yo tibber [--mode] [--homeIDFile] [--APIKeyFile] [--filePath] [--user] [--pwfile] el Fetches home electricity price data โœ…
yo timer [--minutes] [--seconds] [--hours] [--list] [--sound] Set a timer โœ…
yo toilet Flush the toilet โœ…
๐Ÿงฉ Miscellaneous
yo btc [--filePath] [--user] [--pwfile] Crypto currency BTC price tracker โœ…
yo chain --a --b [--c] [--d] Chain commands with 'samt'. Splits and dispatches each part sequentially. โœ…
yo chat --text No fwendz? Let's chat yo! โœ…
yo duckPUCK-ha [--mode] [--team] [--stat] [--count] [--dataDir] puck-ha [๐Ÿ’๐Ÿฆ†] - Your Personal Hockey Assistant! - Expert commentary and analyzer specialized on Hockey Allsvenskan (SWE). Analyzing games, scraping scoreboards and keeping track of all dates and numbers. โœ…
yo duckPUCK-shl [--mode] [--team] [--stat] [--count] [--dataDir] puck, shl [๐Ÿ’๐Ÿฆ†] - Your Personal Hockey Assistant! - Expert commentary and analyzer specialized on Swedish Hockey League (SHL). Analyzing games, scraping scoreboards and keeping track of all dates and numbers. โœ…
yo hockeyGames [--type] [--days] [--league] [--team] [--home] [--away] [--json] [--dataDir] [--debug] [--check] hag Hockey Assistant. Provides Hockey Allsvenskan data and deliver analyzed natural language responses (TTS). โœ…
yo invokeai --prompt [--host] [--port] [--outputDir] [--width] [--height] [--steps] [--cfgScale] [--seed] [--model] genimg AI generated images powered by InvokeAI โœ…
yo joke [--jokeFile] Duck says s funny joke. โœ…
yo pokemon [--pokemon] [--ball] [--dex] [--iv] [--ev] [--stats] [--level] [--many] [--question] [--ribbon] [--eggMoves] [--move] [--ability] [--shiny] [--ha] [--em] [--ribbons] [--listMissing] [--shinyLeft] [--speak] Search user defined Pokรฉmon using PokรฉNix. โœ…
yo post [--postalCodeFile] [--postalCode] Check for the next postal delivery day. (Sweden) โœ…
yo qr --input [--icon] [--output] Create fun randomized QR codes from input. ๐Ÿ“›
yo reminder [--about] [--list] [--clear] [--user] [--pwfile] remind Reminder Assistant โœ…
yo shop-list [--operation] [--item] [--list] [--mqttUser] [--mqttPWFile] Shopping list management โœ…
yo suno --prompt [--genre] mg AI generated lyrics and music files powered by Suno โœ…
yo time Tells time, day, date & week โœ…
yo until [--condition] [--body] [--every] [--max_minutes] Repeat a command until a condition becomes true. โœ…
yo wait [--duration] Sleep before continue. โœ…
yo while --condition --body [--every] [--max_minutes] Repeat a command while a condition holds. โœ…
yo xmr [--filePath] [--user] [--pwfile] Crypto currency XMR price tracker โœ…
yo yo --phrase Say after me โœ…
๐Ÿงน Maintenance
yo clean gc Run a total garbage collection: Removes old NixOS generations, empty trash, flush tmp files, whipes cache and runs a docker prune ๐Ÿ“›
yo duckTrace [--script] [--host] [--errors] [--monitor] log View duckTrace logs quick and quack, unified logging system โœ…
yo health hc Check system health report for tihs host & ESP32 devices. Returns JSON structured response and forwards to zigduck. โœ…

โ“ Detailed Help

For specific command help: `yo --help` `yo -h`


๐Ÿ’ฌ Comments?

Nix Talk? Or just say tiny flake sucks?
That's cool!
I am all ears. ๐Ÿ‘€


Matrix Discord Email

Note

Im not blind.
I just can't see. ๐Ÿง‘โ€๐Ÿฆฏ


โ˜•

Sponsors Buy Me a Coffee

๐Ÿฆ†๐Ÿง‘โ€๐Ÿฆฏ says โฎž Hi! I'm QuackHack-McBlindy!
๐Ÿฆ†๐Ÿง‘โ€๐Ÿฆฏ says โฎž Like my work?
Buy me a coffee, or become a sponsor.
Thanks for supporting open source/hungry developers โ™ฅ๏ธ๐Ÿฆ†!

I'll stop qwackin' here, qwack..