Implement volume control for boxy via Home Assistant
Add remote volume control for the living room media center (boxy) that controls the Denon AVR through Home Assistant instead of local audio. - Create plasma-manager-kodi role with custom KDE hotkeys for volume up/down/mute that execute shell scripts - Scripts retrieve HA token from KDE Wallet and send API requests to control media_player.denon_avr_s970h_2 - Add retry logic (3 attempts) and error notifications - Enable KDE Wallet PAM integration for automatic wallet unlock - Configure KDE settings: Breeze Dark theme, disable screen locking, empty session restore - Add kconfig package for debugging tools
This commit is contained in:
@@ -152,9 +152,9 @@
|
||||
system = "x86_64-linux";
|
||||
modules = nixosModules ++ [
|
||||
./machines/boxy/configuration.nix
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
{
|
||||
home-manager.users.johno = import ./home/home-media-center.nix;
|
||||
home-manager.users.kodi = import ./home/home-kodi.nix;
|
||||
home-manager.extraSpecialArgs = { inherit system; };
|
||||
}
|
||||
];
|
||||
|
||||
28
home/home-kodi.nix
Normal file
28
home/home-kodi.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{ pkgs, globalInputs, system, ... }:
|
||||
|
||||
{
|
||||
# Home Manager configuration for kodi user on boxy
|
||||
# Focused on media center volume control via Home Assistant
|
||||
|
||||
home.username = "kodi";
|
||||
home.homeDirectory = "/home/kodi";
|
||||
home.stateVersion = "24.05";
|
||||
|
||||
# Enable minimal roles for kodi user
|
||||
home.roles = {
|
||||
base.enable = true;
|
||||
plasma-manager-kodi.enable = true;
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
kdePackages.kconfig
|
||||
];
|
||||
|
||||
targets.genericLinux.enable = true;
|
||||
home.sessionVariables = {};
|
||||
home.sessionPath = [];
|
||||
|
||||
imports = [
|
||||
./roles
|
||||
];
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
./launchers
|
||||
./media
|
||||
./office
|
||||
./plasma-manager-kodi
|
||||
./sync
|
||||
./tmux
|
||||
./emacs
|
||||
|
||||
199
home/roles/plasma-manager-kodi/default.nix
Normal file
199
home/roles/plasma-manager-kodi/default.nix
Normal file
@@ -0,0 +1,199 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.home.roles.plasma-manager-kodi;
|
||||
|
||||
# Define the volume control scripts as derivations
|
||||
volumeUpScript = pkgs.writeShellScript "avr-volume-up" ''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configuration
|
||||
HA_URL="https://home-assistant.johnogle.info"
|
||||
ENTITY_ID="media_player.denon_avr_s970h_2"
|
||||
MAX_RETRIES=3
|
||||
|
||||
# Read token from KDE Wallet and strip whitespace
|
||||
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Send volume up command with retry logic
|
||||
for i in $(seq 1 $MAX_RETRIES); do
|
||||
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"entity_id\": \"$ENTITY_ID\"}" \
|
||||
"$HA_URL/api/services/media_player/volume_up" 2>&1)
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Wait before retry (except on last attempt)
|
||||
if [ $i -lt $MAX_RETRIES ]; then
|
||||
sleep 0.5
|
||||
fi
|
||||
done
|
||||
|
||||
# All retries failed
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to increase volume after $MAX_RETRIES attempts"
|
||||
exit 1
|
||||
'';
|
||||
|
||||
volumeDownScript = pkgs.writeShellScript "avr-volume-down" ''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configuration
|
||||
HA_URL="https://home-assistant.johnogle.info"
|
||||
ENTITY_ID="media_player.denon_avr_s970h_2"
|
||||
MAX_RETRIES=3
|
||||
|
||||
# Read token from KDE Wallet and strip whitespace
|
||||
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Send volume down command with retry logic
|
||||
for i in $(seq 1 $MAX_RETRIES); do
|
||||
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"entity_id\": \"$ENTITY_ID\"}" \
|
||||
"$HA_URL/api/services/media_player/volume_down" 2>&1)
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Wait before retry (except on last attempt)
|
||||
if [ $i -lt $MAX_RETRIES ]; then
|
||||
sleep 0.5
|
||||
fi
|
||||
done
|
||||
|
||||
# All retries failed
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to decrease volume after $MAX_RETRIES attempts"
|
||||
exit 1
|
||||
'';
|
||||
|
||||
volumeMuteScript = pkgs.writeShellScript "avr-volume-mute" ''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configuration
|
||||
HA_URL="https://home-assistant.johnogle.info"
|
||||
ENTITY_ID="media_player.denon_avr_s970h_2"
|
||||
MAX_RETRIES=3
|
||||
|
||||
# Read token from KDE Wallet and strip whitespace
|
||||
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get current mute state
|
||||
STATE_RESPONSE=$(${pkgs.curl}/bin/curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"$HA_URL/api/states/$ENTITY_ID")
|
||||
|
||||
CURRENT_MUTE=$(echo "$STATE_RESPONSE" | ${pkgs.jq}/bin/jq -r '.attributes.is_volume_muted // false')
|
||||
|
||||
# Toggle: if currently muted (true), unmute (false), and vice versa
|
||||
if [ "$CURRENT_MUTE" = "true" ]; then
|
||||
NEW_MUTE="false"
|
||||
NOTIFY_MSG="Unmuted"
|
||||
else
|
||||
NEW_MUTE="true"
|
||||
NOTIFY_MSG="Muted"
|
||||
fi
|
||||
|
||||
# Send mute toggle command with retry logic
|
||||
for i in $(seq 1 $MAX_RETRIES); do
|
||||
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"entity_id\": \"$ENTITY_ID\", \"is_volume_muted\": $NEW_MUTE}" \
|
||||
"$HA_URL/api/services/media_player/volume_mute" 2>&1)
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Wait before retry (except on last attempt)
|
||||
if [ $i -lt $MAX_RETRIES ]; then
|
||||
sleep 0.5
|
||||
fi
|
||||
done
|
||||
|
||||
# All retries failed
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to toggle mute after $MAX_RETRIES attempts"
|
||||
exit 1
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.home.roles.plasma-manager-kodi = {
|
||||
enable = mkEnableOption "KDE Plasma volume control for kodi user via Home Assistant";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
programs.plasma = {
|
||||
enable = true;
|
||||
overrideConfig = true;
|
||||
|
||||
# Disable default kmix volume shortcuts to prevent conflicts
|
||||
shortcuts.kmix = {
|
||||
"increase_volume" = "none";
|
||||
"decrease_volume" = "none";
|
||||
"mute" = "none";
|
||||
};
|
||||
|
||||
# Define custom volume control commands with key bindings
|
||||
hotkeys.commands = {
|
||||
"volume-up-avr" = {
|
||||
name = "Volume Up AVR";
|
||||
key = "Volume Up";
|
||||
command = toString volumeUpScript;
|
||||
};
|
||||
|
||||
"volume-down-avr" = {
|
||||
name = "Volume Down AVR";
|
||||
key = "Volume Down";
|
||||
command = toString volumeDownScript;
|
||||
};
|
||||
|
||||
"volume-mute-avr" = {
|
||||
name = "Mute Toggle AVR";
|
||||
key = "Volume Mute";
|
||||
command = toString volumeMuteScript;
|
||||
};
|
||||
};
|
||||
|
||||
# KDE Settings customization
|
||||
configFile = {
|
||||
# Session restore settings
|
||||
"ksmserverrc"."General"."loginMode" = "emptySession";
|
||||
|
||||
# Screen locking settings
|
||||
"kscreenlockerrc"."Daemon"."Autolock" = false;
|
||||
"kscreenlockerrc"."Daemon"."LockOnResume" = false;
|
||||
|
||||
# Theme settings
|
||||
"kdeglobals"."KDE"."LookAndFeelPackage" = "org.kde.breezedark.desktop";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -26,11 +26,20 @@ with lib;
|
||||
enable = true;
|
||||
autologin = true;
|
||||
wayland = true;
|
||||
jellyfinScaleFactor = 2.5;
|
||||
jellyfinScaleFactor = 1.0;
|
||||
};
|
||||
nfs-mounts.enable = true;
|
||||
users.enable = true;
|
||||
};
|
||||
|
||||
# Enable KDE Wallet PAM integration for auto-unlock
|
||||
security.pam.services.sddm = {
|
||||
kwallet = {
|
||||
enable = true;
|
||||
package = pkgs.kdePackages.kwallet-pam;
|
||||
};
|
||||
};
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
Reference in New Issue
Block a user