Ubuntu 12.04: SToggle – HDMI sound toggle/switch

I’m a newb when it comes to scripting and pretty much anything Linux related. But even so, I’m a bit proud of this script because now I can toggle hdmi audio on my laptop from either my main user account or guest account (that, and the fact that I spent 5 hours trying to find a way to get the script to work)! 😀

So, apparently my udev rules were working. The problem was the script. I learned that running scripts in a terminal, in the background, or by udev isn’t the same; environment variables are different and when you use ‘who’ or ‘w’ in a script that isn’t running in the terminal, it’ll display nothing. I guess in older versions of Ubuntu, when you used
USER="$(who | grep :0 | cut -f 1 -d ' ' | head -n 1)"
a line would show for the gnome-session (tty7)? But as far as I can tell, it wasn’t showing in Ubuntu 12.04 (and apparently 11.10 because in my Ubuntu 11.10 VM, it didn’t show up either). Technically, the script (MToggle) works if you had a terminal opened on TTY opened (probably why I thought it worked). But since I had a terminal opened at most times, I just assumed the script worked.

In 11.10 and 12.04, you could get around this by just putting your actual username for USER, but then it wouldn’t work if you had other users that used the same computer. But then again, the script was made for laptops in mind and I doubt it would work for desktops because it depends on the change of hdmi status… I’m not sure.

But what about guest accounts? What about changing usernames? I didn’t like the fact that I was just using my username in the script to get it to work. So I started searching the filesystem for anything that could tell me about the active user logged on. Eventually I found a file called “database” in “/var/run/ConsoleKit” which had info about “x11_display”, which one was active, and the UID of that corresponded to the displays. So the script:

#!/bin/bash
# Sound Toggle
# By Charles Cruz
#
# The following script toggles the between laptop speakers and hdmi audio (if detected).
# Version 1.0

USERID="$(cat /var/run/ConsoleKit/database | grep -B 6 is_active=true | grep uid= | cut -f 2 -d '=')"
USER="$(grep $USERID /etc/passwd | cut -f 1 -d ':')"
HDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"

if [ "${HDMI_STATUS}" = connected ]; then
sudo -u $USER pactl set-card-profile 0 output:hdmi-surround
else
sudo -u $USER pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
fi

exit 0

Steps:

  1. In a terminal:
    sudo gedit /etc/udev/rules.d/hdmi.rules
  2. Copy, paste, then save:
    SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/SToggle"
  3. In the terminal:
    sudo udevadm control --reload-rules
  4. In the terminal:
    sudo gedit /usr/local/bin/SToggle
  5. Copy and paste the script above and then save it.
  6. In the terminal:
    sudo chmod 755 /usr/local/bin/SToggle

And that should do it! Hope it works!

Advertisements

Ubuntu 12.04 and udev… weird….

I jumped the gun and updated to 12.04 a few weeks ago. Everything’s great and I’m somewhat settled in. I like how the screen toggles now to an external monitor when connected via HDMI automatically. However, I realized that the sound profile still doesn’t toggle with it. No biggy; just had to edit that old script I had earlier and add the udev rule.
However, after testing, I realized it wasn’t toggling automatically; I’d plug in my hdmi cable, check the sound profile via sound settings, and it’s still stuck on my laptop speakers! Was it my script? Couldn’t have been; I tested it and it did change the sound profile. My only guess was udev.
Using:

udevadm monitor

I checked to see if I messed up my udev rule. Now here’s where things get weird. As I monitored udev, my script magically worked! But after I closed out the terminal, it stopped working… I’m confused… I need to look into this more.

Ubuntu 11.10 – Automatic HDMI Toggle (with audio!)

This took me forever (well, three days)! I didn’t really know what I was doing for the most part – I never heard of or used xrandr, xbacklight, and udev rules. After hours of googling and through trial and error, I somehow managed to get this script together and working.

A few notes though:

  • I made this for my laptop and specifically for a HDMI connection. Haven’t tested anything else like VGA (but I put it there in my script as an option just in case I ever wanted to try it out).
  • This only toggles between one display. So if more than one external monitor connection is present (such as HDMI or VGA), according to the order of if/then statements in the script, only the HDMI connection is used.
  • Most of my googling brought up solutions for external/dedicated graphics cards from AMD or Nvidia. HOWEVER, this script has only been tested on my laptop which has integrated graphics (Intel HD 3000).
  • This was tested on Ubuntu 11.10 on an HP-DM4-2191US laptop.

Thanks to this wiki, I learned how to create a udev rule.
/etc/udev/rules.d/hdmi.rules
SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/MToggle-udev"

Here’s my script (may need to adjust some stuff such as paths and the index/name of the audio profile):
/usr/local/bin/MToggle-udev
#!/bin/sh
#
# Charles Cruz
# Monitor Toggle
# The following script toggles between the internal monitor and an external monitor.
#
# Version 1.3
# Export Xauthority and display
USER="$(who | grep :0\) | cut -f 1 -d ' ')"
export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=:0
########### Settings ###########
# Use 'xrandr' to find these
DP="DP1"
VGA="VGA1"
HDMI="HDMI1"
INTERNAL_DISPLAY="LVDS1"
# Check /sys/class/drm for the exact location
DP_STATUS="$(cat /sys/class/drm/card0-DP-1/status)"
VGA_STATUS="$(cat /sys/class/drm/card0-VGA-1/status)"
HDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"
# Backlight settings
BACKLIGHT_BATTERY=15
BACKLIGHT_AC=50
# Do no change!
EXTERNAL_DISPLAY=""
# Check to see if the external display is connected
if [ "${DP_STATUS}" = connected ]; then
EXTERNAL_DISPLAY=$DP
fi
if [ "${VGA_STATUS}" = connected ]; then
EXTERNAL_DISPLAY=$VGA
fi
if [ "${HDMI_STATUS}" = connected ]; then
EXTERNAL_DISPLAY=$HDMI
fi
# The external display is connected
if [ "$EXTERNAL_DISPLAY" != "" ]; then
# Set the display settings
xrandr --output $INTERNAL_DISPLAY --off        # Turn off internal display
xrandr --output $EXTERNAL_DISPLAY --auto     # Turn on external display
# If connected via HDMI, change the sound profile to output HDMI audio
if [ $EXTERNAL_DISPLAY=$HDMI ]; then
sudo -u $USER pactl set-card-profile 0 output:hdmi-surround
fi
# The external display is not connected
else
# Restore internal display
xrandr --output $EXTERNAL_DISPLAY --off        # Turn off internal display
xrandr --output $INTERNAL_DISPLAY --auto
# Restore default battery/power brightness
cat /proc/acpi/ac_adapter/AC/state | grep "on-line"
if [ $? -eq 0 ]; then
xbacklight -set    $BACKLIGHT_AC        # Power cable is connected
else
xbacklight -set    $BACKLIGHT_BATTERY    # Power cable is not connected
fi
# Restore laptop sound profile
sudo -u $USER pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
fi
exit 0

Afterwards, in a terminal I made sure the script was executable and that I update the udev rules:
sudo udevadm control --reload-rules
sudo chmod 755 /usr/local/bin/MToggle-udev

and done! Now it should work!