Search code examples
linuxbashperltcpsqueezebox

Turn on AMP with CEC from TCP packets recieved


Firstly I have a working solution but its prone to failure.

I would like to turn on my amp from Logitech Squeeze Center.

  • The server is running on 192.168.0.30
  • The Player is in the Dining Room running on 192.168.0.31
  • The player is connected via HDMI to the amp

I have been able to send CEC commands from the player to the Amp to turn it on.

I further developed (with help from snippets of code from around the web) the solution so that the interface in SqueezeCenter, when the power button has been pressed sends a TCP packet to the player where the player runs a bash script and in turn sends a CEC command to the AMP - powering it on.

Power button for the player

The Perl plugin that sends the packet when the relevant players power button is pressed Note I have it set to just send packets to the specific dining room player - but I would like it in the future to find the IP of the player that had its power button pressed and send a packet to that instead.

package Plugins::PowerMonitor::Plugin;

use strict;

use IO::Socket;
use Slim::Utils::Log;
use Slim::Control::Request;

my $log = Slim::Utils::Log->addLogCategory(
    {   category     => 'plugin.powerMonitor',
        defaultLevel => 'ERROR',
        description  => getDisplayName(),
    }
);

sub getFunctions {
    return '';
}

sub getDisplayName {
    return 'PLUGIN_POWER_MONITOR';
}

sub initPlugin {
    $log->debug("initPlugin");
    # Subscribe to power events
    Slim::Control::Request::subscribe( \&powerCallback, [ ['power'] ] );
}

sub shutdownPlugin {
    $log->debug("shutdownPlugin");
    Slim::Control::Request::unsubscribe( \&powerCallback );
}

sub powerCallback {
    $log->debug("powerCallback");

    my $request = shift;
    my $client = $request->client() || return;

    my $sock = IO::Socket::INET->new(
        Proto    => 'tcp',
        PeerPort => 6500,
        PeerAddr => '192.168.0.31',
    );
    if ( !$sock ) {
        $log->error("Could not create socket: $!");
        return;
    }

    my $msg = $client->id() . ':' . $client->name() . ':' . $client->power() . $/;
    $log->debug($msg);
    my $rc = $sock->send($msg);
    if ( !$rc ) {
        $log->error("Send error: $!");
    }
}

The Bash script on the player - this sits as a daemon on the player listening for the packet on port 6500:

#!/bin/bash

netcat -lk 6500 | while read line
do
    if echo $line | grep -q 'Dining_Room:1'
    then
        powerStatus=$(echo "pow 5" | cec-client -s -d 1 |grep "power status" |cut -d ' ' -f 3)

        if [ "$powerStatus" = "standby" ]; then
            echo "Powering On Amp....."
            echo "on 5" | cec-client -s -d 1
        fi
    elif echo $line | grep -q 'Dining_Room:0'
    then
        powerStatus=$(echo "pow 5" | cec-client -s -d 1 |grep "power status" |cut -d ' ' -f 3)

        if [ "$powerStatus" = "on" ]; then
            echo "Powering Off Amp....."
            echo "standby 5" | cec-client -s -d 1
        fi
    fi
done

A very hair brained elaborate setup but this is what I have to work with in terms of Perl plugins for Squeezebox.

I would like to know if there is a more stable way of doing this and what would that be - could I tweak the current setup to make it more stable - could I change the Perl script to write a file on the player instead of sending a packet and using netcat (this seams to be the place it breaks)

I don't know Perl but am quite versed with Linux and bash scripting

UPDATE 29/08/2014: On the topic of it sending to the IP of the player it looks like it instantiates a $client and that has a method of name - I wonder what else is available in there - man I wish I knew Perl.


Solution

  • I decided to change my approach so that the server would SSH into the player and run the relevant script.

    Initial tests show this is way more reliable than netcat. I couldn't get the key auth to work so had to put a password in the script.

    Perl Script:

    package Plugins::PowerMonitor::Plugin;
    
    use strict;
    use warnings;
    
    use IO::Socket;
    
    use Slim::Utils::Log;
    use Slim::Control::Request;
    
    use Net::SSH::Any;
    
    my $log = Slim::Utils::Log->addLogCategory( {
            category     => 'plugin.powerMonitor',
            defaultLevel => 'ERROR',
            description  => getDisplayName(),
    });
    
    sub getFunctions {
            return '';
    }
    
    sub getDisplayName {
            return 'PLUGIN_POWER_MONITOR';
    }
    
    sub initPlugin {
            $log->debug("initPlugin");
            # Subscribe to power events
            Slim::Control::Request::subscribe(
                    \&powerCallback,
                    [['power']]
            );
    }
    
    sub shutdownPlugin {
            $log->debug("shutdownPlugin");
            Slim::Control::Request::unsubscribe( \&powerCallback );
    }
    
    sub powerCallback {
            $log->debug("powerCallback");
    
            my $request = shift;
            my $client  = $request->client() || return;
    
    my $host = "192.168.0.31";
    
    my $user = "pi";
    my $pass = "myPassword";
    
    my $ssh = Net::SSH::Any->new($host, user => $user, password => $pass);
    
    if ($ssh->error) {
    $log->error("whee...something wrong here: " . $ssh->error);
    }
    else {
            my @out = $ssh->capture('nohup /home/pi/ampOn > foo.out 2> foo.err < /dev/null &');
    $log->debug("@out");
    }
    
    }
    

    Altered bash script:

    #!/bin/bash
    
                    if [ ! -f /tmp/ampIsOn ]
                     then
                            powerStatus=$(echo "pow 5" | cec-client -s -d 1 |grep "power status" |cut -d ' ' -f 3)
    
                            if [ "$powerStatus" = "standby" ]; then
                                    echo "Powering On Amp....."
                                    echo "on 5" | cec-client -s -d 1
                                    touch /tmp/ampIsOn
                            fi
    
                    else
                            powerStatus=$(echo "pow 5" | cec-client -s -d 1 |grep "power status" |cut -d ' ' -f 3)
    
                            if [ "$powerStatus" = "on" ]; then
                                    echo "Powering Off Amp....."
                                    echo "standby 5" | cec-client -s -d 1
                                    rm /tmp/ampIsOn
                            fi
                    fi
    

    I wont mark as correct yet in case someone has some better solutions or hints.