This is a automagiduckically updated README.md
Caution
Do not blindly run this flake.
That's my job. 🧑🦯
Here lives home machines configurations,
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 🦆✨
Nix declarative configuration style, custom modules evaluated dynamically for each host.
Home Manager - No ducking way! I just auto symlink ./home to /home
Zigbee and smart home tightly integrated with Nix. For not just a declarative house but also deployable apartments.
Not only that - voice assistant is LIGHTNIGHT FAST! (ms) ⚡🏆
- 97 qwacktastic scripts in /bin - 58 scripts have voice commands.
- 2440 dynamically generated regex patterns - makes 294355045 phrases available as commands.
- Smart Home Nix Style - Managing 2 TV's, 41 devices & 6 scenes.
- Natural Language support with complete voice pipeline
- Infra as everyday accessibility
- Yubikey encrypted deployment system
- Self Documenting
List would get long, very quackly.
perhaps a more suitable question would be:
"What makes this configuration common?"
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" "users" "pungkula" "adbusers" "audio" "2000" ];
hashedPassword = "$y$j9T$m8hPD36i1VMaO5rurbZ4j0$KpzQyat.F6NoWFKpisEj77TvpN2wBGB8ezd26QoKDj6";
matrix = "";
mobileDevices = {
iphone = {
pubkey = "UFB0T1Y/uLZi3UBtEaVhCi+QYldYGcOZiF9KKurC5Hw=";
wgip = "10.0.0.7"
};
tablet = {
pubkey = "ETRh93SQaY+Tz/F2rLAZcW7RFd83eofNcBtfyHCBWE4=";
wgip = "10.0.0.8"
};
};
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" "gpu/amd" "audio" ];
networking = [ "default" "pool" ];
programs = [ "default" "thunar" "firefox" "vesktop" ];
services = [ "ssh" "default" "adb" "backup" "cache" "keyd" "jelly" "duck-tv" ];
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/1np4cfqil5jh04zmscj3i6h2zvh9yqvv-bibata-cursors-2.0.7";
size = 32
};
enable = false;
fonts = {
monospace = "Fira Code";
packages = [ "/nix/store/k4s2ckig2pyi2lzzaxmh8wcwbq7n7pz3-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/5ncf05fvvy7zmb2azprzq1qhymwh733h-papirus-icon-theme-20250201"
};
name = "gtk3.css";
styles = "/nix/store/fy9zcsirdc47lyshwmfk1flpc4mgrxvw-source/modules/themes/css/gtk3.css"
};Define Zigbee-devices, scenes, automations, tv's, channels, etc at `config.house`.
# dotfiles/modules/myHouse.nix ⮞ https://github.com/quackhack-mcblindy/dotfiles
{ # 🦆 says ⮞ my house - qwack
config, # 🦆 says ⮞ more info ⮞ https://quackhack-mcblindy.github.io/blog/house/index.html
lib,
self,
pkgs,
...
} : let # 🦆 duck say ⮞ icon map
icons = {
light = {
ceiling = "mdi:ceiling-light";
strip = "mdi:light-strip";
spotlight = "mdi:spotlight";
bulb = "mdi:lightbulb";
bulb_color = "mdi:lightbulb-multiple";
desk = "mdi:desk-lamp";
floor = "mdi:floor-lamp";
wall = "mdi:wall-sconce-round";
chandelier = "mdi:chandelier";
pendant = "mdi:vanity-light";
nightlight = "mdi:lightbulb-night";
strip_rgb = "mdi:led-strip-variant";
reading = "mdi:book-open-variant";
candle = "mdi:candle";
ambient = "mdi:weather-night";
};
sensor = {
motion = "mdi:motion-sensor";
smoke = "mdi:smoke-detector";
water = "mdi:water";
contact = "mdi:door";
temperature = "mdi:thermometer";
humidity = "mdi:water-percent";
};
remote = "mdi:remote";
outlet = "mdi:power-socket-eu";
dimmer = "mdi:toggle-switch";
pusher = "mdi:gesture-tap-button";
blinds = "mdi:blinds";
};
health = lib.mapAttrs (hostName: _: {
enable = true;
description = "Health Check: ${hostName}";
topic = "zigbee2mqtt/health/${hostName}";
actions = [
{
type = "shell";
command = ''
mkdir -p /var/lib/zigduck/health
touch /var/lib/zigduck/health/${hostName}.json
echo "$MQTT_PAYLOAD" > /var/lib/zigduck/health/${hostName}.json
'';
}
];
}) self.nixosConfigurations;
in { # 🦆 duck say ⮞ qwack
house = {
# 🦆 says ⮞ ROOM CONFIGURATION
rooms = {
bedroom.icon = "mdi:bed";
hallway.icon = "mdi:door";
kitchen.icon = "mdi:food-fork-drink";
livingroom.icon = "mdi:sofa";
wc.icon = "mdi:toilet";
other.icon = "mdi:misc";
};
# 🦆 says ⮞ DASHBOARD CONFIOGURATION
dashboard = {
statusCards = {
xmr = {
enable = true;
title = "XMR";
icon = "fab fa-monero";
color = "#ff6600";
filePath = "/var/lib/zigduck/xmr.json";
jsonField = "current_price";
format = "${value}";
detailsJsonField = "24h_change";
detailsFormat = "24h: {value}%";
};
btc = {
enable = true;
title = "BTC";
icon = "fab fa-bitcoin";
color = "#ff6600";
filePath = "/var/lib/zigduck/btc.json";
jsonField = "current_price";
format = "${value}";
detailsJsonField = "24h_change";
detailsFormat = "24h: {value}%";
};
energy = {
enable = true;
title = "Energy";
icon = "fas fa-bolt";
color = "#4caf50";
filePath = "/var/lib/zigduck/energy.json";
jsonField = "current_price";
format = "{value} SEK/kWh";
detailsJsonField = "monthly_usage";
detailsFormat = "Usage: {value} kWh";
};
};
pages = {
"4" = {
icon = "fas fa-notes-medical";
title = "health";
files = { health = "/var/lib/zigduck/health"; };
css = ''
.health-page .container,
.health-page .content,
.health-page > div {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
.page[data-page] {
width: 100% !important;
max-width: 100% !important;
}
.health-page {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.health-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
justify-items: center;
}
.health-card {
background: var(--card-bg);
border-radius: 12px;
padding: 20px;
box-shadow: var(--card-shadow);
width: 100%;
max-width: 350px;
}
.health-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 10px;
flex-direction: column;
text-align: center;
gap: 10px;
}
.health-hostname {
font-size: 1.2rem;
font-weight: bold;
color: var(--primary);
}
.health-status {
display: grid;
gap: 8px;
}
.health-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.health-label {
color: var(--gray);
font-size: 0.9rem;
}
.health-value {
font-weight: 600;
}
.status-good { color: #2ecc71; }
.status-warning { color: #f39c12; }
.status-critical { color: #e74c3c; }
/* 🦆 says ⮞ Responsive design */
@media (max-width: 768px) {
.health-page {
padding: 10px;
}
.health-grid {
grid-template-columns: 1fr;
gap: 10px;
}
.health-card {
max-width: 100%;
}
}
'';
code = ''
<h1 style="text-align:center;">Machines Health</h1>
<div id="healthContainer" class="health-grid"></div>
<script>
async function loadHealthData() {
try {
const response = await fetch('/health/');
const text = await response.text();
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(text, 'text/html');
const links = Array.from(htmlDoc.querySelectorAll('a'));
const jsonFiles = links
.map(link => link.href)
.filter(href => href.endsWith('.json'))
.map(href => href.split('/').pop());
console.log('Found health files:', jsonFiles);
const container = document.getElementById('healthContainer');
container.innerHTML = "";
for (const file of jsonFiles) {
try {
const healthResponse = await fetch('/health/' + file);
const healthData = await healthResponse.json();
createHealthCard(healthData, container);
} catch (error) {
console.error('Error loading health file:', file, error);
}
}
} catch (error) {
console.error('Error loading health directory:', error);
document.getElementById('healthContainer').innerHTML =
'<div class="error">Unable to load health data</div>';
}
}
function createHealthCard(data, container) {
const card = document.createElement('div');
card.className = 'health-card';
const status = calculateOverallStatus(data);
card.innerHTML = `
<div class="health-card-header">
<div class="health-hostname"><strong><h1>''${data.hostname}</h1></strong></div><br>
<div class="health-uptime">''${data.uptime}</div>
</div>
<div class="health-status">
<div class="health-item">
<span class="health-label"><strong>CPU:</strong></span>
<span class="health-value ''${getCPUStatusClass(data.cpu_usage)}">''${data.cpu_usage}%</span>
</div>
<div class="health-item">
<span class="health-label"><strong>Memory:</strong></span>
<span class="health-value ''${getMemoryStatusClass(data.memory_usage)}">''${data.memory_usage}%</span>
</div>
<div class="health-item">
<span class="health-label"><strong>CPU 🌡️:</strong></span>
<span class="health-value ''${getTempStatusClass(data.cpu_temperature)}">''${data.cpu_temperature}</span>
</div>
''${createDiskUsageHTML(data.disk_usage)}
''${createDiskTempHTML(data.disk_temperature)}
</div>
`;
container.appendChild(card);
}
function calculateOverallStatus(data) {
if (data.cpu_usage > 90 || data.memory_usage > 90) return 'critical';
if (data.cpu_usage > 80 || data.memory_usage > 80) return 'warning';
return 'good';
}
function getCPUStatusClass(usage) {
if (usage > 80) return 'status-critical';
if (usage > 60) return 'status-warning';
return 'status-good';
}
function getMemoryStatusClass(usage) {
if (usage > 90) return 'status-critical';
if (usage > 75) return 'status-warning';
return 'status-good';
}
function getTempStatusClass(temp) {
const tempValue = parseFloat(temp);
if (tempValue > 70) return 'status-critical';
if (tempValue > 60) return 'status-warning';
return 'status-good';
}
function createDiskUsageHTML(diskUsage) {
if (!diskUsage) return "";
return Object.entries(diskUsage).map(([device, usage]) => `
<div class="health-item">
<span class="health-label"><strong>Disk</strong> (''${device}):</span>
<span class="health-value ''${getDiskStatusClass(usage)}">''${usage}</span>
</div>
`).join("");
}
function createDiskTempHTML(diskTemp) {
if (!diskTemp) return "";
return Object.entries(diskTemp).map(([device, temp]) => `
<div class="health-item">
<span class="health-label"><strong>Disk 🌡️</strong>(''${device}):</span>
<span class="health-value ''${getTempStatusClass(temp)}">''${temp}</span>
</div>
`).join("");
}
function getDiskStatusClass(usage) {
const usageValue = parseFloat(usage);
if (usageValue > 90) return 'status-critical';
if (usageValue > 80) return 'status-warning';
return 'status-good';
}
document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('healthContainer')) {
loadHealthData();
setInterval(loadHealthData, 30000);
}
});
</script>
'';
};
};
};
# 🦆 ⮞ ZIGBEE ⮜ 🐝
zigbee = {
# 🦆 says ⮞ encrypted zigbee network key
networkKeyFile = config.sops.secrets.z2m_network_key.path;
# 🦆 says ⮞ mosquitto authentication
mosquitto = {
username = "mqtt";
passwordFile = config.sops.secrets.mosquitto.path;
};
# 🦆says⮞ coordinator configuration
coordinator = {
vendorId = "10c4";
productId = "ea60";
symlink = "zigbee"; # 🦆 says ⮞ diz symlinkz da serial port to /dev/zigbee
};
# 🦆 says ⮞ when motion triggers lights
darkTime = {
enable = true;
after = "14";
before = "9";
duration = "900";
};
# 🦆 ⮞ AUTOMATIONS ⮜
automations = {
# 🦆 says ⮞ there are 6 different automation types
# 🦆 says ⮞ + a greeting automation
greeting = {
enable = true;
awayDuration = "7200";
greeting = "Borta bra, hemma bäst. Välkommen idiot! ";
delay = "10";
sayOnHost = "desktop";
};
# 🦆 says ⮞ 1. mqtt triggered automations
mqtt_triggered = {
xmr = {
enable = true;
description = "Writes XMR data to file for dashboard";
topic = "zigbee2mqtt/crypto/xmr/price";
actions = [
{
type = "shell";
command = ''
touch /var/lib/zigduck/xmr.json
echo "$MQTT_PAYLOAD" > /var/lib/zigduck/xmr.json
'';
}
];
};
btc = {
enable = true;
description = "Writes BTC data to file for dashboard";
topic = "zigbee2mqtt/crypto/btc/price";
actions = [
{
type = "shell";
command = ''
touch /var/lib/zigduck/btc.json
echo "$MQTT_PAYLOAD" > /var/lib/zigduck/btc.json
'';
}
];
};
energy = {
enable = true;
description = "Writes tibber data to file for dashboard";
topic = "zigbee2mqtt/tibber/energy";
actions = [
{
type = "shell";
command = ''
touch /var/lib/zigduck/energy.json
echo "$MQTT_PAYLOAD" > /var/lib/zigduck/energy.json
current_price=$(echo "$MQTT_PAYLOAD" | jq -r '.current_price' | sed 's/"//g')
# 🦆says⮞ notify if high energy price
if [ $(echo "$current_price > 2.0" | bc -l) -eq 1 ]; then
yo notify "⚡ High energy price: $current_price SEK/kWh"
fi
'';
}
];
};
} // health;
# 🦆 says ⮞ 2. room action automations
room_actions = {
hallway = {
door_opened = [];
door_closed = [];
};
bedroom = {
# 🦆 says ⮞ default actions already configured - room lights will turn on upon motion (if darkTime)
motion_detected = [
{
type = "scene";
scene = "Chill Scene";
}
];
motion_not_detected = [
{
type = "mqtt";
topic = "zigbee2mqtt/Sänggavel/set";
message = ''{"state":"OFF", "brightness": 80}'';
}
];
};
};
# 🦆 says ⮞ 3. global actions automations
global_actions = {
leak_detected = [
{
type = "shell";
command = "yo notify '🚨 WATER LEAK DETECTED!'";
}
];
smoke_detected = [
{
type = "shell";
command = "yo notify '🔥 SMOKE DETECTED!'";
}
];
};
# 🦆 says ⮞ 4. dimmer actions automations
dimmer_actions = {
bedroom = {
on_hold_release = {
enable = true;
description = "Turn off all configured light devices";
extra_actions = [];
override_actions = [];
};
off_hold_release = {
enable = true;
description = "Turn off all configured light devices";
extra_actions = [];
override_actions = [
{
type = "scene";
command = "dark";
}
];
};
};
};
# 🦆 says ⮞ 5. time based automations
time_based = {};
# 🦆 says ⮞ 6. presence based automations
presence_based = {};
};
# 🦆 ⮞ DEVICES ⮜
devices = {
# 🦆 says ⮞ Kitchen
"0x0017880103ca6e95" = { # 🦆 says ⮞ 64bit IEEE adress (this is the unique device ID)
friendly_name = "Dimmer Switch Kök"; # 🦆 says ⮞ simple human readable friendly name
room = "kitchen"; # 🦆 says ⮞ bind to group
type = "dimmer"; # 🦆 says ⮞ set a custom device type
icon = icons.dimmer;
endpoint = 1; # 🦆 says ⮞ endpoint to call the device on
batteryType = "CR2450"; # 🦆 says ⮞ optional yo
};
"0x0017880102f0848a" = {
friendly_name = "Spotlight kök 1";
room = "kitchen";
type = "light";
icon = icons.light.spotlight;
endpoint = 11;
};
"0x0017880102f08526" = { friendly_name = "Spotlight Kök 2"; room = "kitchen"; type = "light"; icon = icons.light.spotlight; endpoint = 11; };
"0x0017880103a0d280" = { friendly_name = "Uppe"; room = "kitchen"; type = "light"; icon = icons.light.strip; endpoint = 11; supports_color = true; };
"0x0017880103e0add1" = { friendly_name = "Golvet"; room = "kitchen"; type = "light"; icon = icons.light.strip; endpoint = 11; supports_color = true; };
"0xa4c13873044cb7ea" = { friendly_name = "Kök Bänk Slinga"; room = "kitchen"; type = "light"; icon = icons.light.strip; endpoint = 11; };
"0x70ac08fffe9fa3d1" = { friendly_name = "Motion Sensor Kök"; room = "kitchen"; type = "motion"; icon = icons.sensor.motion; endpoint = 1; batteryType = "CR2032"; };
"0xa4c1380afa9f7f3e" = { friendly_name = "Smoke Alarm Kitchen"; room = "kitchen"; type = "sensor"; icon = icons.sensor.smoke; endpoint = 1; };
"0x0c4314fffe179b05" = { friendly_name = "Fläkt"; room = "kitchen"; type = "outlet"; icon = icons.outlet; endpoint = 1; };
# 🦆 says ⮞ LIVING ROOM
"0x0017880104f78065" = { friendly_name = "Dimmer Switch Vardagsrum"; room = "livingroom"; type = "dimmer"; icon = icons.dimmer; endpoint = 1; batteryType = "CR2450"; };
"0x00178801037e754e" = { friendly_name = "Takkrona 1"; room = "livingroom"; type = "light"; icon = icons.light.chandelier; endpoint = 1; supports_color = true; };
"0x0017880103c73f85" = { friendly_name = "Takkrona 2"; room = "livingroom"; type = "light"; icon = icons.light.chandelier; endpoint = 1; supports_color = true; };
"0x0017880103f94041" = { friendly_name = "Takkrona 3"; room = "livingroom"; type = "light"; icon = icons.light.chandelier; endpoint = 1; supports_color = true; };
"0x0017880103c753b8" = { friendly_name = "Takkrona 4"; room = "livingroom"; type = "light"; icon = icons.light.chandelier; endpoint = 1; supports_color = true; };
"0x54ef4410003e58e2" = { friendly_name = "Roller Shade"; room = "livingroom"; type = "blind"; icon = icons.blinds; endpoint = 1; };
"0x0017880104540411" = { friendly_name = "PC"; room = "livingroom"; type = "light"; icon = icons.light.spotlight; endpoint = 11; supports_color = true; };
"0x0017880102de8570" = { friendly_name = "Rustning"; room = "livingroom"; type = "light"; icon = icons.light.spotlight; endpoint = 11; supports_color = true; };
"0x540f57fffe85c9c3" = { friendly_name = "Water Sensor"; room = "livingroom"; type = "sensor"; icon = icons.sensor.water; endpoint = 1; };
# 🦆 says ⮞ HALLWAY
"0x00178801021311c4" = { friendly_name = "Motion Sensor Hall"; room = "hallway"; type = "motion"; icon = icons.sensor.motion; endpoint = 1; batteryType = "AAA"; };#⮜ AAA-AWESOME 🦆
"0x00158d00053ec9b1" = { friendly_name = "Door Sensor Hall"; room = "hallway"; type = "sensor"; icon = icons.sensor.contact; endpoint = 1; };
"0x0017880103eafdd6" = { friendly_name = "Tak Hall"; room = "hallway"; type = "light"; icon = icons.light.ceiling; supports_color = true; endpoint = 11; };
"0x000b57fffe0e2a04" = { friendly_name = "Vägg"; room = "hallway"; type = "light"; icon = icons.light.wall; supports_temperature = true; endpoint = 1; };
# 🦆 says ⮞ WC
"0x001788010361b842" = { friendly_name = "WC 1"; room = "wc"; type = "light"; icon = icons.light.ceiling; supports_temperature = true; endpoint = 11; };
"0x0017880103406f41" = { friendly_name = "WC 2"; room = "wc"; type = "light"; icon = icons.light.ceiling; supports_temperature = true; endpoint = 11; };
# 🦆 says ⮞ BEDROOM
"0xa4c13832742c96f7" = { friendly_name = "Robot Arm 1"; room = "bedroom"; type = "pusher"; endpoint = 11; icon = icons.pusher; batteryType = "CR02"; };
"0xa4c138387966b58d" = { friendly_name = "Robot Arm 2"; room = "bedroom"; type = "pusher"; endpoint = 11; icon = icons.pusher; batteryType = "CR02"; };
"0xa4c1380c0a35052e" = { friendly_name = "Robot Arm 3"; room = "bedroom"; type = "pusher"; endpoint = 11; icon = icons.pusher; batteryType = "CR02"; };
"0xa4c1381e74b6d2e6" = { friendly_name = "Robot Arm 4"; room = "bedroom"; type = "pusher"; endpoint = 11; icon = icons.pusher; batteryType = "CR02"; };
"0x0017880104f77d61" = { friendly_name = "Dimmer Switch Sovrum"; room = "bedroom"; type = "dimmer"; icon = icons.dimmer; endpoint = 1; batteryType = "CR2450"; };
"0x0017880106156cb0" = { friendly_name = "Taket Sovrum 1"; room = "bedroom"; type = "light"; icon = icons.light.ceiling; endpoint = 11; supports_color = true; };
"0x0017880103c7467d" = { friendly_name = "Taket Sovrum 2"; room = "bedroom"; type = "light"; icon = icons.light.ceiling; endpoint = 11; supports_color = true; };
"0x0017880109ac14f3" = { friendly_name = "Sänglampa"; room = "bedroom"; type = "light"; icon = icons.light.bulb; endpoint = 11; supports_color = true; };
"0x0017880104051a86" = { friendly_name = "Sänggavel"; room = "bedroom"; type = "light"; icon = icons.light.strip; endpoint = 11; supports_color = true; };
"0xf4b3b1fffeaccb27" = { friendly_name = "Motion Sensor Sovrum"; room = "bedroom"; type = "motion"; icon = icons.sensor.motion; endpoint = 1; batteryType = "CR2032"; };
"0x0017880103f44b5f" = { friendly_name = "Dörr"; room = "bedroom"; type = "light"; icon = icons.light.strip; endpoint = 11; supports_color = true; };
"0x00178801001ecdaa" = { friendly_name = "Bloom"; room = "bedroom"; type = "light"; icon = icons.light.desk; endpoint = 11; supports_color = true; };
# 🦆 says ⮞ MISCELLANEOUS
"0xa4c1382553627626" = { friendly_name = "Power Plug"; room = "other"; type = "outlet"; icon = icons.outlet; endpoint = 1; };
"0xa4c138b9aab1cf3f" = { friendly_name = "Power Plug 2"; room = "other"; type = "outlet"; icon = icons.outlet; endpoint = 1; };
"0x000b57fffe0f0807" = { friendly_name = "IKEA 5 Dimmer"; room = "other"; type = "remote"; icon = icons.remote; endpoint = 1; };
"0x70ac08fffe6497be" = { friendly_name = "On/Off Switch 1"; room = "other"; type = "remote"; icon = icons.remote; endpoint = 1; batteryType = "CR2032"; };
"0x70ac08fffe65211e" = { friendly_name = "On/Off Switch 2"; room = "other"; type = "remote"; icon = icons.remote; endpoint = 1; batteryType = "CR2032"; };
};
# 🦆 ⮞ SCENES ⮜
scenes = {
# 🦆 says ⮞ Scene name
"Duck Scene" = {
# 🦆 says ⮞ Device friendly_name
"PC" = { # 🦆 says ⮞ Device state
state = "ON";
brightness = 200;
color = { hex = "#00FF00"; };
};
};
# 🦆 says ⮞ Scene 2
"Chill Scene" = {
"PC" = { state = "ON"; brightness = 200; color = { hex = "#8A2BE2"; }; }; # 🦆 says ⮞ Blue Violet
"Golvet" = { state = "ON"; brightness = 200; color = { hex = "#40E0D0"; }; }; # 🦆 says ⮞ Turquoise
"Uppe" = { state = "ON"; brightness = 200; color = { hex = "#FF69B4"; }; }; # 🦆 says ⮞ Hot Pink
"Spotlight kök 1" = { state = "OFF"; brightness = 200; color = { hex = "#FFD700"; }; }; # 🦆 says ⮞ Gold
"Spotlight Kök 2" = { state = "OFF"; brightness = 200; color = { hex = "#FF8C00"; }; }; # 🦆 says ⮞ Dark Orange
"Taket Sovrum 1" = { state = "ON"; brightness = 200; color = { hex = "#00CED1"; }; }; # 🦆 says ⮞ Dark Turquoise
"Taket Sovrum 2" = { state = "ON"; brightness = 200; color = { hex = "#9932CC"; }; }; # 🦆 says ⮞ Dark Orchid
"Bloom" = { state = "ON"; brightness = 200; color = { hex = "#FFB6C1"; }; }; # 🦆 says ⮞ Light Pink
"Sänggavel" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 1" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 2" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 3" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 4" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
};
"Green D" = {
"PC" = { state = "ON"; brightness = 200; color = { hex = "#00FF00"; }; };
"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 = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 2" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 3" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
"Takkrona 4" = { state = "ON"; brightness = 200; color = { hex = "#7FFFD4"; }; }; # 🦆 says ⮞ Aquamarine
};
"dark" = { # 🦆 says ⮞ eat darkness... lol YO! You're as blind as me now! HA HA!
"Bloom" = { state = "OFF"; transition = 10; };
"Dörr" = { state = "OFF"; transition = 10; };
"Golvet" = { state = "OFF"; transition = 10; };
"Kök Bänk Slinga" = { state = "OFF"; transition = 10; };
"PC" = { state = "OFF"; transition = 10; };
"Rustning" = { state = "OFF"; transition = 10; };
"Spotlight Kök 2" = { state = "OFF"; transition = 10; };
"Spotlight kök 1" = { state = "OFF"; transition = 10; };
"Sänggavel" = { state = "OFF"; transition = 10; };
"Sänglampa" = { state = "OFF"; transition = 10; };
"Tak Hall" = { state = "OFF"; transition = 10; };
"Taket Sovrum 1" = { state = "OFF"; transition = 10; };
"Taket Sovrum 2" = { state = "OFF"; transition = 10; };
"Uppe" = { state = "OFF"; transition = 10; };
"Vägg" = { state = "OFF"; transition = 10; };
"WC 1" = { state = "OFF"; transition = 10; };
"WC 2" = { state = "OFF"; transition = 10; };
"Takkrona 1" = { state = "OFF"; transition = 10; };
"Takkrona 2" = { state = "OFF"; transition = 10; };
"Takkrona 3" = { state = "OFF"; transition = 10; };
"Takkrona 4" = { state = "OFF"; transition = 10; };
};
"dark-fast" = { # 🦆 says ⮞ eat darkness... NAO!
"Bloom" = { state = "OFF"; };
"Dörr" = { 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"; };
"Sänglampa" = { 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"; };
};
"max" = { # 🦆 says ⮞ let there be light
"Bloom" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Dörr" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Golvet" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Kök Bänk Slinga" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"PC" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Rustning" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Spotlight Kök 2" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Spotlight kök 1" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Sänggavel" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Sänglampa" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Tak Hall" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Taket Sovrum 1" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Taket Sovrum 2" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Uppe" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Vägg" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"WC 1" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"WC 2" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Takkrona 1" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Takkrona 2" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Takkrona 3" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
"Takkrona 4" = { state = "ON"; brightness = 255; color = { hex = "#FFFFFF"; }; };
};
};
};
# 🦆 ⮞ TV ⮜
# 🦆says⮞ configure TV devices with: room, ip, apps & channel information
tv = {
# 🦆 says ⮞ Livingroom
shield = {
enable = true;
room = "livingroom";
ip = "192.168.1.223";
apps = {
telenor = "se.telenor.stream/.MainActivity";
tv4 = "se.tv4.tv4playtab/se.tv4.tv4play.ui.mobile.main.BottomNavigationActivity";
};
channels = {
"1" = {
name = "SVT1";
id = 1; # 🦆 says ⮞ adb channel ID
# 🦆 says ⮞ OR
# stream_url = "https://url.com/";
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/";
};
"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_doown && 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 ⮞ Bedroom
arris = {
enable = true;
room = "bedroom";
ip = "192.168.1.152";
apps = {
telenor = "se.telenor.stream/.MainActivity ";
tv4 = "se.tv4.tv4playtab/se.tv4.tv4play.ui.mobile.main.BottomNavigationActivity";
};
channels = {
"1" = {
id = 1;
name = "SVT1";
icon = ./themes/icons/tv/1.png;
scrape_url = "https://tv-tabla.se/tabla/svt1/";
};
"2" = {
id = 2;
name = "SVT2";
icon = ./themes/icons/tv/2.png;
scrape_url = "https://tv-tabla.se/tabla/svt2/";
};
"3" = {
id = 3;
name = "Kanal 3";
icon = ./themes/icons/tv/3.png;
scrape_url = "https://tv-tabla.se/tabla/tv3/";
};
"4" = {
id = 4;
name = "TV4";
icon = ./themes/icons/tv/4.png;
scrape_url = "https://tv-tabla.se/tabla/tv4/";
};
"5" = {
id = 5;
name = "TV5";
icon = ./themes/icons/tv/5.png;
scrape_url = "https://tv-tabla.se/tabla/kanal_5/";
};
"6" = {
id = 6;
name = "Kanal 6";
icon = ./themes/icons/tv/6.png;
scrape_url = "https://tv-tabla.se/tabla/tv6/";
};
"7" = {
id = 7;
name = "Sjuan";
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 = "nav_down && nav_down && nav_right && nav_right && nav_center";
scrape_url = "https://tv-tabla.se/tabla/tv4_hockey/";
};
"14" = {
id = 14;
name = "TV4 Sport Live 1";
icon = ./themes/icons/tv/14.png;
cmd = "nav_down && nav_down && nav_right && nav_right && nav_center";
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 = "nav_down && nav_down && nav_right && nav_right && nav_center";
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 = "nav_down && nav_down && nav_right && nav_right && nav_center";
scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_3/";
};
"17" = {
id = 17;
name = "TV 4 Sport Live 4";
icon = ./themes/icons/tv/17.png;
cmd = "nav_down && nav_down && nav_right && nav_right && nav_center";
scrape_url = "https://tv-tabla.se/tabla/tv4_sport_live_4/";
};
};
};
};
};}
And you'll get a dashboard for your devices generated and found at http://localhost:13337
The dashboard currently gives you:
- Advanced zigbee device control
- Remote for your Android TV devices
- Set scenes
- Access to
yo dothrough both an text input field aswell as microphone - and more...
I like my flakes tiny & ny modules dynamically loaded,
# dotfiles/flake.nix ⮞ https://github.com/quackhack-mcblindy/dotfiles
{
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";
caddy-duckdns.url = "github:QuackHack-McBlindy/nix-caddy-duckdns";
installer.url = "github:QuackHack-McBlindy/auto-installer-nixos";
};
outputs = inputs @ { self, systems, nixpkgs, ... }:
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; };
hosts = lib.mapHosts ./hosts;
specialArgs = { pkgs = system: nixpkgs.legacyPackages.${system}; };
packages = lib.mapModules ./packages import;
devShells = lib.mapModules ./devShells (path: import path);
};} # 🦆 duck say ⮞ flakes all set, with no debating — next nix file awaiting, ducks be there waitin'View Flake Outputs
git+file:///home/pungkula/dotfiles?ref=refs/heads/main&rev=8901ec868ee3dbd16c532980f68227a20cbc63b7
├───devShells
│ ├───aarch64-linux
│ │ ├───android 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)
│ └───x86_64-linux
│ ├───android: 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'
├───nixosConfigurations
│ ├───desktop: NixOS configuration
│ ├───homie: NixOS configuration
│ ├───laptop: NixOS configuration
│ └───nasty: NixOS configuration
├───overlays
│ └───noisereduce: Nixpkgs overlay
└───packages
├───aarch64-linux
│ ├───health omitted (use '--all-systems' to show)
│ ├───installer omitted (use '--all-systems' to show)
│ ├───jellyfin omitted (use '--all-systems' to show)
│ ├───say omitted (use '--all-systems' to show)
│ ├───tv omitted (use '--all-systems' to show)
│ └───yo-bitch omitted (use '--all-systems' to show)
└───x86_64-linux
├───health: package 'health'
├───installer: package 'nixos-auto-installer-24.05.20240406.ff0dbd9-x86_64-linux.iso'
├───jellyfin: package 'jellyfin'
├───say: package 'say'
├───tv: package 'tv'
└───yo-bitch: package 'yo-bitch'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=syncPlug 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 laptopAny builds after first deployment will use local cached binaries for enhanced build time.
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 <command> [arguments]
The yo CLI supports flexible parameter parsing through two primary mechanisms:
# Named Parameters
$ yo deploy --host laptop --flake /home/pungkula/dotfiles
# Positional Parameters
$ yo deploy laptop /home/pungkula/dotfiles
# Scripts can also be executed with natural language text by typing:
$ yo do "is laptop overheating"
# Natural language voice commands are also supported, say:
"yo bitch reboot the laptop"
# If the server is not running, it can be manually started with:
$ yo transcribe
$ yo wakeSet default values for your parameters to have them marked [optional]
| Command Syntax | Aliases | Description | VoiceReady |
|---|---|---|---|
| 🖥️ System Management | |||
| yo deploy --host [--flake] [--user] [--repo] [--port] [--test] | Build and deploy a NixOS configuration to a remote host. Bootstraps, builds locally, activates remotely, and auto-tags the generation. | ✅ | |
| yo dev [--devShell] | Start development enviorment | 📛 | |
| yo dry | Build and deploy a NixOS configuration to a remote host. Bootstraps, builds locally, activates remotely, and auto-tags the generation. | 📛 | |
| yo esp [--device] [--serialPort] [--ota] [--otaPort] [--OTAPwFile] [--wifiSSID] [--wifiPwFile] [--mqttHost] [--mqttUser] [--mqttPwFile] [--transcriptionHostIP] | Declarative firmware deployment tool for ESP32 boards with built-in version control. | 📛 | |
| yo espOTA | Updates ESP32 devices over the air. | 📛 | |
| 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 Nix OS system configuration. ('!' to test) | ✅ |
| ⚡ 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 fzf | f | Interactive fzf search for file content with quick edit & jump to line | 📛 |
| yo google --search [--apiKeyFile] [--searchIDFile] | g | Perform web search on google | ✅ |
| yo hitta --search | Locate a persons address with help of Hitta.se | ✅ | |
| yo img2phone --image | Send images to an iPhone | 📛 | |
| yo pull [--flake] [--host] | Pull the latest changes from your dotfiles repo. Resets tracked files to origin/main but keeps local extras. | ✅ | |
| 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 scp --host [--path] [--username] [--downloadPath] | Move files between hosts interactively | 📛 | |
| yo update-readme [--readmePath] | Updates the documentation in README.md | 📛 | |
| 🌍 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 [--arrival] [--departure] [--type] [--apikeyPath] | Public transportation helper. Fetches current bus and train schedules. (Sweden) | ✅ | |
| yo weather [--location] [--day] [--condition] [--locationPath] | weat | Weather Assistant. Ask anything weather related (3 day forecast) | ✅ |
| 🌐 Networking | |||
| yo api [--host] [--port] [--dir] | Simple API for collecting system data | 📛 | |
| yo block --url [--blocklist] | ad | Block URLs using DNS | 📛 |
| yo ip-updater [--token1] [--token2] [--token3] | DDNS updater | ✅ | |
| yo notify [--text] [--title] [--icon] [--url] [--group] [--sound] [--volume] [--copy] [--autoCopy] [--level] [--encrypt] [--base_urlFile] [--deviceKeyFile] | Send custom push to iOS devices | 📛 | |
| 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 | call | 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] [--playedFile] | API caller and playlist manager for latest Swedish news from SR. | ✅ | |
| yo transcode [--directory] | trans | Transcode media files | 📛 |
| yo tv [--typ] [--search] [--device] [--season] [--shuffle] [--tvshowsDir] [--moviesDir] [--musicDir] [--musicvideoDir] [--videosDir] [--podcastDir] [--audiobookDir] [--youtubeAPIkeyFile] [--webserver] [--defaultPlaylist] [--favoritesPlaylist] [--max_items] [--mqttUser] [--mqttPWFile] | remote | Android TV Controller. Fuzzy search all media types and creates playlist and serves over webserver for casting. Fully conttrollable. | ✅ |
| yo tv-guide [--search] [--channel] [--jsonFilePath] | tvg | TV-guide assistant.. | ✅ |
| yo tv-scraper [--epgFilePath] [--jsonFilePath] [--flake] | tvs | Scrapes web for tv-listing data. Builds EPG and generates HTML. | 📛 |
| yo vlc [--add] [--addDir] [--remove] [--list] [--shuffle] [--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 cancel [--input] | Cancel coammands microphone recording sent to transcription. | ✅ | |
| yo do [--input] [--fuzzy] [--dir] [--build] [--realtime] | brain | Brain (do) is a Natural Language to Shell script translator that generates dynamic regex patterns at build time for defined yo.script sentences. At runtime it runs exact and fuzzy pattern matching with automatic parameter resolution and seamless execution | 📛 |
| yo do-bash --input [--fuzzy] | [🦆🧠] yo do - The Brain of this repository. Natural language to Shell script translator with dynamic regex matching and automatic parameter resolutiion with some fuzzy on top of that. Written in Bash (slower, but more dope🦆, don't ya think?) | 📛 | |
| yo espaudio | WIP! ESP32 audio development | 📛 | |
| yo kill-mic | Kill mic-stream by port with voice | ✅ | |
| yo memory [--show] [--record] [--good] [--tail] [--reset] | stats | Memory is stats and metrics that acts as contexual awareness for the natural langugage processor. | 📛 |
| yo mic [--port] [--host] [--seconds] | [🦆🎙️] Trigger microphone recording sent to transcription. | 📛 | |
| yo mic-stream [--chunk] [--silence] [--silenceLevel] | Stream microphone audio to WS chunk transcription | 📛 | |
| yo say --text [--model] [--modelDir] [--silence] [--host] [--blocking] [--file] [--caf] | Text to speech with built in language detection and automatic model downloading | ✅ | |
| yo sleep --time | Waits for specified time (seconds). Useful in command chains. | ✅ | |
| yo tests [--input] [--stats] | Extensive automated sentence testing for the NLP | ✅ | |
| yo tests-rs [--input] [--stats] [--fuzzy] [--dir] [--build] [--realtime] | Extensive automated sentence testing for the NLP () | 📛 | |
| yo train --phrase | Trains the NLP module. Correct misclassified commands and update NLP patterns | 📛 | |
| yo transcribe [--port] [--model] [--language] [--beamSize] [--gpu] [--cert] [--key] | Transcription server-side service. Sit and waits for audio that get transcribed and returned. | 📛 | |
| yo transcription-ws | WebSocket server for real-time transcription streaming to NLP | 📛 | |
| yo wake [--threshold] [--cooldown] [--sound] [--remoteSound] [--redisHost] [--redis_pwFIle] | Run Wake word detection for audio recording and transcription | 📛 | |
| 🛖 Home Automation | |||
| yo alarm --hours --minutes [--list] [--sound] | wakeup | Set an alarm for a specified time | ✅ |
| yo battery [--device] | Fetch battery level for specified device. | ✅ | |
| yo bed [--part] [--state] | Bed controller | ✅ | |
| yo blinds [--state] | Turn blinds up/down | ✅ | |
| yo chair [--part] [--state] | Chair controller | ✅ | |
| yo duckDash [--host] [--port] [--cert] [--key] | dash | Mobile-first dashboard, unified frontend for Zigbee devices, tv remotes, and other smart home gadgets. Includes DuckCloud page for easy access to your files. (Use WireGuard) | 📛 |
| yo findPhone | Helper for locating Phone | ✅ | |
| yo house [--device] [--state] [--brightness] [--color] [--temperature] [--scene] [--room] [--user] [--passwordfile] [--flake] [--pair] [--cheapMode] | Control lights and other home automatioon devices | ✅ | |
| yo kitchenFan [--state] | Turns kitchen fan on/off | ✅ | |
| yo leaving | Run when leaving house to set away state | 📛 | |
| 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 state [--device] | Fetches the state of the specified device. | ✅ | |
| yo temperatures | Get all temperature values from sensors and return a average value. | ✅ | |
| 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 | ✅ | |
| yo zigduck [--user] [--pwfile] | [🦆🏡] yo zigduck - Home automation system written in Bash | 📛 | |
| yo zigduck-rs [--dir] [--user] [--pwfile] | [🦆🏡] ZigDuck - Home automation system! Devices, scenes, automations -- EVERYTHING is defined using Nix options from the module 'house.nix'. (Written in Rust) | 📛 | |
| 🧩 Miscellaneous | |||
| yo btc [--filePath] [--user] [--pwfile] | Crypto currency BTC price tracker | ✅ | |
| yo chat --text | No fwendz? Let's chat yo! | ✅ | |
| yo duckPUCK [--mode] [--team] [--stat] [--count] [--dataDir] | puck | [🏒🦆] - Your Personal Hockey Assistant! - Expert commentary and analyzer specialized on Hockey Allsvenskan (SWE). Analyzing games, scraping scoreboards and keeping track of all dates annd numbers. | ✅ |
| yo hockeyGames [--type] [--days] [--team] [--dataDir] [--debug] | 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 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 and date | ✅ | |
| yo xmr [--filePath] [--user] [--pwfile] | Crypto currency XMR price tracker | ✅ | |
| 🧹 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 status across your machines. Returns JSON structured responses. | ✅ |
For specific command help:
yo <command> --help
yo <command> -h
Nix Talk? Or just say tiny flake sucks?
That's cool!
I am all ears. 👀
Note
Im not blind.
I just can't see. 🧑🦯