#!/usr/bin/perl =head1 NAME squeezebox_ - plugin to monitor a Logitech Media Server and associated players. =head1 APPLICABLE SYSTEMS Probably any system running Logitech Media Server. Change the host to enable remote monitoring. =head1 CONFIGURATION No configuration should be required if run on the same server as the Logitech Media Server. If the plugin is run from another unit or in a non-default configuration, please use the environment variables 'squeezebox_host' and 'squeezebox_port' to connect. Sample content for plugin-conf.d/ follows: [squeezebox] env.squeezecenter_host 192.168.100.10 env.squeezecenter_port 9095 =head1 INTERPRETATION The "volume" graphs only graphs the player volume levels, not the amplifier or whatever the player may be connected to. =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf suggest =head1 BUGS None known =head1 AUTHOR Bjørn Ruberg =head1 LICENSE GPLv2 =cut #%# family=auto #%# capabilities=autoconf suggest use strict; my $ret = undef; if (! eval "require Net::Telnet;") { $ret = "Net::Telnet not found"; } if (! eval "require URI::Escape;") { $ret = "URI::Escape not found"; } use vars qw ($host $port $config); # Define connection settings, from plugin environment file or use defaults my $host = exists $ENV{'squeezecenter_host'} ? $ENV{'squeezecenter_host'} : "127.0.0.1"; my $port = exists $ENV{'squeezecenter_port'} ? $ENV{'squeezecenter_port'} : "9090"; # Argument handling: autoconf, suggest, update, config if (@ARGV) { if ($ARGV[0] eq "autoconf") { # autoconf for magically self-configuring the plugin if ($ret) { print "no ($ret)"; exit 0; } my $conn = new Net::Telnet (Telnetmode => 0); $conn->open (Host => $host, Port => $port, Errmode => "return"); if ($conn->errmsg) { print "no (No connection on $host port $port)"; exit 0; } else { my $version = ""; my $line = ""; $conn->print("version ?"); $conn->print("exit"); while (($line = $conn->getline) and ($line !~ /exit/)) { $version = $line if $line =~ /version/; } if ($version =~ /^version/) { print "yes"; exit 0; } else { print "no (socket responded but the server didn't respond as expected)"; exit 0; } } } elsif ($ARGV[0] eq "suggest") { print "I am a multigraph plugin, and suggest is not required\n"; exit 0; } elsif ($ARGV[0] eq "update") { # For scheduled inventory rescan, add this plugin to a cron job # with the argument "update" Adjust the interval to your own tempo # for adding/deleting music. This equals a "Look for new and # changed media files" rescan from the webUI. # # example: 5 * * * * /usr/share/munin/plugins/squeezebox update my $conn = new Net::Telnet (Telnetmode => 0); $conn->open (Host => $host, Port => $port, Errmode => "return"); if ($conn->errmsg) { print $conn->errmsg, "\n";; exit 1; } else { $conn->print("rescan"); $conn->print("exit"); exit 0; } } elsif ($ARGV[0] eq "config") { # Sets $config value for using in the main execution cycle $config = 1; } } # We're keeping the socket open for all checks my $conn = new Net::Telnet (Telnetmode => 0); $conn->open (Host => $host, Port => $port); use URI::Escape; # use Encode qw (from_to); use Text::Iconv; my $converter = Text::Iconv->new("UTF-8", "LATIN1"); # $converted = $converter->convert("Text to convert"); # First all the simple readings foreach my $attr (qw(albums artists genres songs)) { $conn->print ("info total ${attr} ?"); my $line = uri_unescape($conn->getline); if ($line =~ /^info total ${attr} (\d+)$/) { my $number = $1; print "multigraph squeezebox_${attr}\n"; if ($config) { print "graph_title Number of ${attr}\n"; print "graph_scale no\n"; print "graph_category radio\n"; print "${attr}.label ${attr}\n"; } else { print "${attr}.value $number\n"; } } } # years $conn->print ("years"); if (uri_unescape($conn->getline) =~ /^years\s+count:(\d+)/) { my $no_of_years = $1; $conn->print ("years 0 $no_of_years"); my @years = split (" ", uri_unescape($conn->getline)); print "multigraph squeezebox_years\n"; if ($config) { # config run print "graph_title Albums per year\n"; print "graph_category radio\n"; print "graph_args --base 1000 -l 0\n"; foreach my $year (@years) { if ($year =~ /year\:(\d+)/) { print "y" . $1 . ".label $1\n"; print "y" . $1 . ".draw AREASTACK\n"; } } } else { # regular run foreach my $year (@years) { if ($year =~ /(year\:\d+)/) { $conn->print ("albums 0 0 $1"); # albums 0 0 year:2007 count:13 my $line = uri_unescape ($conn->getline); if ($line =~ /^.*year\:(\d+) count\:(\d+)$/) { print "y${1}.value ${2}\n"; } } } } } # mixer volume and signalstrength foreach my $attr ("signalstrength", "mixer volume") { # The plugin reports as squeezebox_volume while the command is # "mixer volume". (my $attr_print = $attr) =~ s/mixer //g; print "multigraph squeezebox_${attr_print}\n"; if ($config) { print "graph_title " . ucfirst ($attr) . "\n"; print "graph_category radio\n"; } $conn->print ("player count ?"); (my $no_of_players = uri_unescape ($conn->getline)) =~ s/player count\s+//; chomp $no_of_players; my $id; for ($id = 0; $id < $no_of_players; $id++) { $conn->print ("player id ${id} ?"); (my $mac = uri_unescape ($conn->getline)) =~ s/player id \d+ //g; chomp ($mac); (my $mac_print = 'm' . $mac) =~ s/\://g; if ($config) { $conn->print ("player name ${mac} ?"); (my $name = uri_unescape ($conn->getline)) =~ s/player name ${mac} //g; chomp $name; print "${mac_print}.label ", $converter->convert($name), "\n"; } else { $conn->print ("${mac} ${attr} ?"); (my $value = uri_unescape ($conn->getline)) =~ s/^.* //g; chomp $value; print "${mac_print}.value $value\n"; } } }