diff --git a/fhem/FHEM/71_LISTENLIVE.pm b/fhem/FHEM/71_LISTENLIVE.pm
new file mode 100644
index 000000000..be48b4929
--- /dev/null
+++ b/fhem/FHEM/71_LISTENLIVE.pm
@@ -0,0 +1,1136 @@
+# $Id: $
+##############################################################################
+#
+# 71_LISTENLIVE.pm
+# An FHEM Perl module for controlling ListenLive-enabled Mediaplayers
+# via network connection.
+#
+# Copyright: betateilchen ®
+# e-mail : fhem.development@betateilchen.de
+#
+# This file is part of fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+##############################################################################
+# Changelog:
+# 2013-07-21 Logging vereinheitlich
+# Meldungen komplett auf englisch umgestellt
+# pod (EN) erstellt
+#
+##############################################################################
+
+package main;
+
+#use strict;
+use warnings;
+use POSIX;
+use CGI qw(:standard);
+use IO::Socket;
+use IO::Socket::INET;
+use MIME::Base64;
+use Time::HiRes qw(gettimeofday sleep usleep nanosleep);
+use HttpUtils;
+use feature qw/say switch/;
+
+
+sub LISTENLIVE_Set($@);
+sub LISTENLIVE_Get($@);
+sub LISTENLIVE_Define($$);
+sub LISTENLIVE_GetStatus($;$);
+sub LISTENLIVE_Undefine($$);
+
+###################################
+sub
+LISTENLIVE_Initialize($)
+{
+ my ($hash) = @_;
+
+ $hash->{GetFn} = "LISTENLIVE_Get";
+ $hash->{SetFn} = "LISTENLIVE_Set";
+ $hash->{DefFn} = "LISTENLIVE_Define";
+ $hash->{UndefFn} = "LISTENLIVE_Undefine";
+
+ $hash->{AttrList} = "do_not_notify:0,1 loglevel:0,1,2,3,4,5 ".
+ $readingFnAttributes;
+}
+
+###################################
+sub
+LISTENLIVE_Set($@)
+{
+ my ($hash, @a) = @_;
+ my $name = $hash->{NAME};
+ my $address = $hash->{helper}{ADDRESS};
+ my $loglevel = GetLogLevel($name, 3);
+ my $result;
+ my $response;
+ my $command;
+
+ return "No Argument given!\n\n".LISTENLIVE_HelpSet() if(!defined($a[1]));
+
+ my $pstat = $hash->{READINGS}{power}{VAL};
+ my $mute = $hash->{READINGS}{mute}{VAL};
+
+# my @b = split(/\./, $a[1]);
+# my $area = $b[0];
+# my $doit = $b[1];
+# if(!defined($doit) && defined($a[2])) { $doit = $a[2]; }
+
+ my $area = $a[1];
+ my $doit = $a[2];
+
+ my $usage = "Unknown argument, choose one of help statusRequest power:on,off audio:volp,volm,mute,unmute cursor:up,down,left,right,enter,exit,home,ok reset:power,mute,menupos app:weather raw user";
+
+ given ($area){
+
+#
+# AREA = user
+# ruft eine userdefinierte Funktion, z.B. aus 99_myUtils.pm auf
+#
+
+ when("user"){
+ if(defined($doit)){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = &{$doit};
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ { return $usage; }
+ break;
+ } # end user
+
+#
+# AREA = raw
+# sendet einfach das per http an das Gerät
+# und schreibt die Rückmeldung in das Reading "rawresult"
+# (hauptsächlich für Debugging vorgesehen)
+#
+
+ when("raw"){
+ if(defined($doit)){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, $doit);
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ }
+ else
+ { return $usage; }
+ break;
+ } # end raw
+
+#
+# AREA = reset ||
+# sendet gnadenlos ein POWER oder MUTE oder setzt
+# die MENUPOS auf 11 (oben links), damit man eine Chance hat,
+# den Gerätestatus zu synchronisieren
+#
+
+ when("reset"){
+ given($doit){
+ when("power"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult","OK");
+ readingsBulkUpdate($hash, "power","???");
+ readingsEndUpdate($hash, 1);
+ break;
+ } # end reset.power
+
+ when("mute"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult","OK");
+ readingsBulkUpdate($hash, "mute","???");
+ readingsEndUpdate($hash, 1);
+ break;
+ } # end reset.mute
+
+ when("menupos"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "HOME");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos","11");
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end reset.menupos
+
+ default:
+ { return $usage; }
+
+ } # end doit
+
+ } # end area.reset
+
+#
+# AREA power |
+# Es wird vor dem Senden geprüft, ob der Befehl Sinn macht,
+# da der gleiche Befehl für das Ein- und Ausschalten
+# verwendet wird.
+# Ein "power on" wird bei einem eingeschalteten Gerät nicht gesendet
+# Ein "power off" wird bei einem ausgeschalteten Gerät nicht gesendet
+# Als Basis für die Entscheidung dient das Reading power
+#
+
+ when("power"){
+ given ($doit){
+ when("on") {
+ if($pstat ne "on"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "POWER");
+ if($result =~ m/OK/)
+ {
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "power","on");
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit, " => device already on!");
+ }
+ break;
+ } # end power.on
+
+ when("off") {
+ if($pstat ne "off")
+ {
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "POWER");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "power","off");
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit, " => device already off!");
+ }
+ break;
+ } # end power.off
+
+ default:
+ { return $usage; }
+
+ } # end power.doit
+
+ } # end area.power
+
+#
+# AREA audio |||
+#
+
+ when("audio"){
+ given($doit){
+
+ when("mute"){
+ if($mute ne "on"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "MUTE");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "mute","on");
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit, "Already muted!");
+ }
+ break;
+ } # end mute
+
+ when("unmute"){
+ if($mute ne "off"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "MUTE");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "mute","off");
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit, "Already unmuted!");
+ }
+ break;
+ } # end unmute
+
+ when("volp"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "VOLp");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end volp
+
+ when("volm"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "VOLm");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit");
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end volm
+
+ default:
+ { return $usage; }
+
+ } # end audio.doit
+
+ } # end area.audio
+
+#
+# AREA cursor ||||home|||
+#
+
+ when("cursor"){
+ given($doit){
+
+ when("up"){
+ Log $loglevel, "LISTENLIVE $name $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "UP");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos",$hash->{READINGS}{menuPos}{VAL}-10);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end up
+
+ when("down"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "DOWN");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos",$hash->{READINGS}{menuPos}{VAL}+10);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end down
+
+ when("left"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "LEFT");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos",$hash->{READINGS}{menuPos}{VAL}-1);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end left
+
+ when("right"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "RIGHT");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos",$hash->{READINGS}{menuPos}{VAL}+1);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end right
+
+ when("home"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "HOME");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsBulkUpdate($hash, "menuPos","11");
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit)
+ }
+ break;
+ } # end home
+
+ when("enter"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "OK");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end enter
+
+ when("ok"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "OK");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ break;
+ } # end ok
+
+ when("exit"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "EXIT");
+ if($result =~ m/OK/){
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult",$result);
+ readingsEndUpdate($hash, 1);
+ return undef;
+ }
+ else
+ {
+ LISTENLIVE_rbuError($hash, $area, $doit);
+ }
+ } # end cursor.exit
+
+ default:
+ { return $usage; }
+
+ } # end cursor.doit
+
+ } # end area.cursor
+
+#
+# AREA app
+#
+
+ when ("app"){
+ given($doit){
+
+ when("weather"){
+ Log $loglevel, "LISTENLIVE $name input: $area $doit";
+ $result = LISTENLIVE_SendCommand($hash, "HOME");
+ select(undef, undef, undef, 0.2);
+ $result = LISTENLIVE_SendCommand($hash, "DOWN");
+ select(undef, undef, undef, 0.2);
+ $result = LISTENLIVE_SendCommand($hash, "DOWN");
+ select(undef, undef, undef, 0.2);
+ $result = LISTENLIVE_SendCommand($hash, "RIGHT");
+ select(undef, undef, undef, 0.2);
+ $result = LISTENLIVE_SendCommand($hash, "OK");
+
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd",$a[1]);
+ readingsBulkUpdate($hash, "lastResult","done");
+ readingsBulkUpdate($hash, "menuPos", "32");
+ readingsEndUpdate($hash, 1);
+ } # end doit.weather
+
+ default:
+ { return $usage; }
+
+ } # end doit
+
+ } # end area.app
+
+ when("statusRequest") { break; } # wird automatisch aufgerufen!
+
+ when("?") { return $usage; }
+
+ when("help") { return LISTENLIVE_HelpSet(); }
+
+ when("present") { break; }
+ when("absent") { break; }
+ when("online") { break; }
+ when("offline") { break; }
+
+ default: { return $usage; }
+
+ } # end area
+
+# Call the GetStatus() Function to retrieve the new values
+ LISTENLIVE_GetStatus($hash, 1);
+
+ return $response;
+}
+
+###################################
+sub
+LISTENLIVE_Get($@){
+ my ($hash, @a) = @_;
+ my $name = $hash->{NAME};
+ my $address = $hash->{helper}{ADDRESS};
+ my $response;
+
+ return "No Argument given" if(!defined($a[1]));
+
+ my @b = split(/\./, $a[1]);
+ my $area = $b[0];
+ my $doit = $b[1];
+
+ my $usage = "Unknown argument $a[1], choose one of help list:commands";
+
+ given($area){
+
+ when("list"){
+
+ given("$doit"){
+
+ when("commands"){
+ $response = LISTENLIVE_HelpGet();
+ break;
+ }
+
+ default: { return $usage; }
+
+ } # end area.list.doit
+
+ } # end area.list
+
+ when("?") { return $usage; }
+
+ when("help"){ $response = LISTENLIVE_HelpGet(); }
+
+ default: { return $usage; }
+
+ } # end area
+
+return $response;
+}
+
+###################################
+sub
+LISTENLIVE_GetStatus($;$){
+
+ my ($hash, $local) = @_;
+ my $name = $hash->{NAME};
+ my $presence;
+
+ $local = 0 unless(defined($local));
+
+ if($hash->{helper}{ADDRESS} ne "none")
+ {
+ $presence = ReadingsVal("pres_".$name,"state","noPresence");
+ }
+ else
+ {
+ $presence = "present";
+ }
+
+ $presence = ReplaceEventMap($name, $presence, 1);
+
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "state", $presence);
+ readingsEndUpdate($hash, 1);
+
+ InternalTimer(gettimeofday()+$hash->{helper}{INTERVAL}, "LISTENLIVE_GetStatus", $hash, 0) unless($local == 1);
+ return $hash->{STATE};
+
+}
+
+#############################
+sub
+LISTENLIVE_Define($$)
+{
+ my ($hash, $def) = @_;
+ my @a = split("[ \t][ \t]*", $def);
+ my $name = $hash->{NAME};
+
+ if(! @a >= 4)
+ {
+ my $msg = "wrong syntax: define LISTENLIVE [:] []";
+ Log 2, $msg;
+ return $msg;
+ }
+
+# Attribut eventMap festlegen (schönere Optik im Frontend)
+ $attr{$name}{"eventMap"} = "absent:offline present:online";
+
+# Adresse in IP und Port zerlegen
+ my @address = split(":", $a[2]);
+ $hash->{helper}{ADDRESS} = $address[0];
+
+# falls kein Port angegeben, Standardport 8080 verwenden
+ $address[1] = "8080" unless(defined($address[1]));
+ $hash->{helper}{PORT} = $address[1];
+
+# falls kein Intervall angegeben, Standardintervall 60 verwenden
+ my $interval = $a[3];
+ $interval = "60" unless(defined($interval));
+ $hash->{helper}{INTERVAL} = $interval;
+
+ if($address[0] ne "none")
+ {
+ # PRESENCE aus device pres_+NAME lesen
+ my $presence = ReadingsVal("pres_".$name,"state","noPresence");
+
+ if($presence eq "noPresence") # PRESENCE nicht vorhanden
+ {
+ $cmd = "pres_$name PRESENCE lan-ping $address[0]";
+ $ret = CommandDefine(undef, $cmd);
+ if($ret)
+ {
+ Log 2, "LISTENLIVE ERROR $ret";
+ }
+ else
+ {
+ Log 3, "LISTENLIVE $name PRESENCE pres_$name created.";
+ }
+ }
+ else
+ {
+ Log 3, "LISTENLIVE $name PRESENCE pres_$name found.";
+ }
+ }
+ else # Gerät ist als dummy definiert
+ {
+ $presence = "present"; # dummy immer als online melden
+ }
+
+ $presence = ReplaceEventMap($name, $presence, 1);
+
+# Readings anlegen und füllen
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","");
+ readingsBulkUpdate($hash, "lastResult","");
+ readingsBulkUpdate($hash, "menuPos","11");
+ readingsBulkUpdate($hash, "mute","???"));
+ readingsBulkUpdate($hash, "power","???"));
+ readingsBulkUpdate($hash, "state",$presence);
+ readingsEndUpdate($hash, 1);
+
+ $hash->{helper}{AVAILABLE} = 1;
+ InternalTimer(gettimeofday()+$hash->{helper}{INTERVAL}, "LISTENLIVE_GetStatus", $hash, 0);
+
+return;
+}
+
+#############################
+sub
+LISTENLIVE_SendCommand($$;$)
+{
+ my ($hash, $command, $loglevel) = @_;
+ my $name = $hash->{NAME};
+ my $address = $hash->{helper}{ADDRESS};
+ my $port = $hash->{helper}{PORT};
+ my $response = "";
+ my $modus = "dummy";
+ my ($socket,$client_socket);
+
+ $loglevel = GetLogLevel($name, 3) unless(defined($loglevel));
+ Log $loglevel, "LISTENLIVE $name command: $command";
+
+ if (Value("$name") eq "online" && $hash->{helper}{ADDRESS} ne "none") { $modus = "online"; }
+ if (Value("$name") eq "offline") { $modus = "offline"; }
+
+ given($modus)
+ {
+ when("online")
+ {
+ #
+ # Create a socket object for the communication with the radio
+ #
+ $socket = new IO::Socket::INET (
+ PeerHost => $address,
+ PeerPort => $port,
+ Proto => 'tcp',
+ ) or die "ERROR in Socket Creation : $!\n";
+
+ #
+ # Send the given command into the socket
+ #
+ $socket->send($command);
+
+ #
+ # get the radio some time to execute the command (300ms )
+ #
+ usleep(30000);
+
+ #
+ # get the answer of the radio
+ #
+ $socket->recv($response, 2);
+
+
+ if($response !~ m/OK/)
+ {
+ Log 2, "LISTENLIVE $name error: $response";
+ }
+ else
+ {
+ Log $loglevel, "LISTENLIVE $name response: $response";
+ }
+
+ $socket->close();
+
+ $hash->{helper}{AVAILABLE} = (defined($response) ? 1 : 0);
+ }
+
+ when("offline")
+ {
+ Log 2, "LISTENLIVE $name error: device offline!";
+ $response = "device offline!";
+ }
+
+ default:
+ {
+ $response = "OK";
+ }
+ }
+
+ return $response;
+}
+
+sub
+LISTENLIVE_rbuError($$;$$)
+{
+ my ($hash, $area, $doit, $parameter) = @_;
+ Log 2, "LISTENLIVE $hash->{NAME} error: $area $doit $parameter";
+
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "lastCmd","$area $doit $parameter");
+ readingsBulkUpdate($hash, "lastResult","Error: $area $doit $parameter");
+ readingsEndUpdate($hash, 1);
+ return undef;
+}
+
+
+sub
+LISTENLIVE_HelpGet()
+{
+my $helptext =
+'get []
+
+
+commandGroup "help"
+get llradio help (show this help page)';
+
+return $helptext;
+}
+
+sub
+LISTENLIVE_HelpSet()
+{
+my $helptext =
+'set []
+
+
+commandGroup "help"
+set llradio help (show this help page)
+
+
+commandGroup "audio"
+set llradio audio mute
+set llradio audio unmute
+set llradio audio volm
+set llradio audio volp
+
+
+commandGroup "cursor"
+set llradio cursor down
+set llradio cursor left
+set llradio cursor up
+set llradio cursor right
+
+set llradio cursor enter
+set llradio cursor exit
+set llradio cursor home
+set llradio cursor ok
+
+
+commandGroup "power"
+set llradio power off
+set llradio power on
+
+
+commandGroup "raw"
+set llradio raw
+
+
+commandGroup "reset"
+set llradio reset menupos
+set llradio reset mute
+set llradio reset power
+
+
+commandGroup "user" (experimental!)
+set llradio user
+
+
+commandGroup "app" (experimental!)
+set llradio app weather
+
+
+commandGroup "statusRequest"
+set llradio statusRequest';
+
+return $helptext;
+}
+
+#############################
+sub
+LISTENLIVE_Undefine($$)
+{
+ my($hash, $name) = @_;
+
+ # Stop the internal GetStatus-Loop and exist
+ RemoveInternalTimer($hash);
+ return undef;
+}
+
+1;
+
+=pod
+=begin html
+LISTENLIVE
+
+
+
+ Define
+
+ define <name> LISTENLIVE <ip-address>[:<port>] [<status_interval>]
+
+
+ This module can control all mediaplayers runnng ListenLive Firmware laufen via a network connection.
+ It can control power state on/off, volume up/down/mute and can send all remomte-control commands.
+
+ The port value is optional. If not defined, standard port 8080 will be used.
+
+ The status_interval value is optional. If not defined, standard interval 60sec will be used.
+
+ Upon the definition of a new LISTENLIVE-device an internal Loop will be defined which will check and update the device readings
+ all seconds to trigger all notify and FileLog entities.
+
+
+ Example:
+
+
+ define llradio LISTENLIVE 192.168.0.10
+
+ define llradio LISTENLIVE 192.168.0.10:8085 120 # with port (8085) und status interval (120 seconds)
+
+
+
+
+ Set-Commands
+
+ set <name> <commandGroup> [<command>] [<parameter>]
+
+ Commands are grouped into commandGroups depending on their functional tasks.
+ The following groups and commands are currently available:
+
+
+commandGroup power
+power on
+power off
+
+commandGroup audio
+audio mute
+audio unmute
+audio volm
+audio volp
+
+commandGroup cursor
+cursor up
+cursor down
+cursor left
+cursor right
+cursor home
+cursor exit
+cursor enter
+cursor ok
+
+commandGroup reset
+reset power
+reset mute
+reset menupos
+
+commandGroup raw
+raw
+
+commandGroup user (experimental)
+user
+
+commandGroup app (experimental)
+app weather
+
+commandGroup help
+help
+
+commandGroup statusRequest
+statusRequest
+
+
+
+
+ Get-Commands
+
+ get <name> <parameter>
+
+ The following parameters are available:
+
+ help - show help-text
+
+
+
+
+
+ Attributes
+
+
+ Generated Readings/Events:
+
+ - lastCmd - last command sent to device
+ - lastResult - last response from device
+ - menuPos - cursor position in main menu (experimental)
+ - mute - current mute state ("on" => muted, "off" => unmuted)
+ - power - current power state
+ - state - current device state (online or offline)
+
+
+ Author's notes
+
+ You need to activate option "remote control settings" -> "network remote control [on]" in your device's settings.
+
+ Upon the device definion a corresponding PRESENCE-entity will be created to evaluate the device availability.
+
+
+
+=end html
+=begin html_DE
+
+LISTENLIVE
+
+
+
+ Define
+
+ define <name> LISTENLIVE <ip-address>[:<port>] [<status_interval>]
+
+
+ Dieses Modul steuert Internetradios, die mit der ListenLive Firmware laufen, über die Netzwerkschnittstelle.
+ Es bietet die Möglichkeit das Gerät an-/auszuschalten, die Lautstärke zu ändern, den Cursor zu steuern,
+ den Receiver "Stumm" zu schalten, sowie alle Fernbedienungskommandos an das Gerät zu senden.
+
+ Die Angabe des TCP-ports ist optional. Fehlt dieser Parameter, wird der Standardwert 8080 verwendet.
+
+ Bei der Definition eines LISTENLIVE-Gerätes wird eine interne Routine in Gang gesetzt, welche regelmäßig
+ (einstellbar durch den optionalen Parameter <status_interval>; falls nicht gesetzt ist der Standardwert 60 Sekunden)
+ den Status des Gerätes abfragt und entsprechende Notify-/FileLog-Geräte triggert..
+
+ Beispiel:
+
+
+ define llradio LISTENLIVE 192.168.0.10
+
+ define llradio LISTENLIVE 192.168.0.10:8085 120 # Mit modifiziertem Port (8085) und Status Interval (120 Sekunden)
+
+
+
+
+ Set-Kommandos
+
+ set <Name> <Befehlsgruppe> [<Befehl>] [<Parameter>]
+
+ Die Befehle zur Steuerung sind weitgehend in Befehlsgruppen eingeordnet, die sich an logischen Funktionsbereichen orientieren.
+ Aktuell stehen folgende Befehlsgruppen und Befehele zur Verfügung:
+
+
+Befehlsgruppe power
+power on
+power off
+
+Befehlsgruppe audio
+audio mute
+audio unmute
+audio volm
+audio volp
+
+Befehlsgruppe cursor
+cursor up
+cursor down
+cursor left
+cursor right
+cursor home
+cursor exit
+cursor enter
+cursor ok
+
+Befehlsgruppe reset
+reset power
+reset mute
+reset menupos
+
+Befehlsgruppe raw
+raw
+
+Befehlsgruppe user (experimentell)
+user
+
+Befehlsgruppe app (experimentell)
+app weather
+
+Befehlsgruppe help
+help
+
+Befehlsgruppe statusRequest
+statusRequest
+
+
+
+
+ Get-Kommandos
+
+ get <Name> <Parameter>
+
+ Aktuell stehen folgende Parameter zur Verfügung:
+
+ help - zeigt einen Hilfetext an
+
+
+
+
+
+ Attribute
+
+
+ Generierte Readings/Events:
+
+ - lastCmd - der letzte gesendete Befehl
+ - lastResult - die letzte Antwort des Gerätes
+ - menuPos - Cursorposition im Hauptmenü (experimentell)
+ - mute - der aktuelle Stumm-Status("on" => Stumm, "off" => Laut)
+ - power - der aktuelle Betriebsstatuse ("on" => an, "off" => aus)
+ - state - der aktuelle Gerätestatus (online oder offline)
+
+
+ Hinweise
+
+ Dieses Modul ist nur nutzbar, wenn die Option "remote control settings" -> "network remote control [on]" in der Firmware aktiviert ist.
+
+ Während der Definition wird automatisch ein passendes PRESENCE angelegt, um die Verfügbarkeit des Gerätes zu ermitteln.
+
+
+
+=end html_DE
+
+=cut