#!/usr/bin/env perl # $Id: connect-bt-headset,v 1.10 2018/07/31 04:10:50 friedman Exp $ # Connect my headset using the bloody fucking bluez5 crapware. package BTExpect ; use strict; use warnings qw(all); use Expect; use base qw(Expect); our $cb = quotemeta ("\033[0;92m"); our $ce = quotemeta ("\033[0m"); sub usleep { select (undef, undef, undef, $_[0]) } sub new { my $type = shift; my $self = $type->SUPER::new (@_); #$self->debug (3); #$self->exp_internal (1); $self->raw_pty (1); $self->spawn ('bluetoothctl'); usleep (.01); $self->SUPER::send ("\n"); return $self; } sub send { my $self = shift; print @_, "\n"; $self->SUPER::send (@_, "\n"); exp_continue; } package main; use strict; use warnings qw(all); sub bt_connect { my $btid = shift; my $e = BTExpect->new; my $succ; $e->expect (10, [ qr/\[${cb}NEW$ce\] Controller.*#/s => sub { $e->send ("power on") } ], [ qr/Agent registered/ => sub { $e->send ("power on") } ], [ qr/Changing power on succeeded/ => sub { $e->send ("connect $btid") } ], [ qr/Connection successful/ => sub { $e->send ("quit"); $succ = 1; } ], [ qr/Failed to connect/ => sub { $e->send ("quit"); $succ = 0; } ], [ qr/\[${cb}DEL$ce\] Controller/ => sub { $e->soft_close; } ]); return $succ; } sub xsystem { print "+ @_\n"; system (@_); exit ($?) unless $? == 0; } sub xbt { print "+ @_\n"; open (my $fh, "-|", @_) || die "exec: $_[0]: $!\n"; local $/ = undef; my $out = <$fh>; close ($fh); print $out; exit ($?) unless $? == 0; return $out; } sub xcat { open (my $fh, $_[0]) || die "open: $_[0]: $!\n"; local $/ = undef; return <$fh>; } sub get_btmac { # Pass in "$ENV{HOME}" rather than just $ENV{HOME}, so that appending to # $_ in map doesn't modify the environment variable itself, just a copy! map { $_ .= "/.btheadset"; if (-f $_) { $_ = xcat ($_); chomp; return $1 if /^\s*([0-9a-f:]+)/mi ; } } ("$ENV{HOME}", "$ENV{HOME}/etc/misc"); return; } sub main { return $? unless bt_connect ($_[0] || get_btmac); my $cards = xbt (qw(pactl list short cards)); my $id = $1 if $cards =~ /^(\d+)\s+bluez_card/m; return 0 unless defined $id; xsystem (qw(pactl set-card-profile), $id, qw(a2dp_sink)); my $sinks = xbt (qw(pactl list short sinks)); $id = $1 if $sinks =~ /^(\d+)\s+bluez_sink/m; return 0 unless defined $id; xsystem (qw(pactl set-default-sink), $id); my $result = $?; # After the new evdev device is added, if the pause button on the headset # is pressed it will reset all the custom xkb bindings. Running this # preemptively seems to avoid that, though I'm not sure why. xsystem (qw(reset-xkb)); return $result; } main (@ARGV); # eof #