From e59789325812d5ee3bea70a357d734ab7beb7aa3 Mon Sep 17 00:00:00 2001
From: markusbloch <>
Date: Fri, 2 Nov 2012 09:17:36 +0000
Subject: [PATCH] new module YAMAHA_AVR added

git-svn-id: https://svn.fhem.de/fhem/trunk@2053 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
 fhem/FHEM/71_YAMAHA_AVR.pm | 344 +++++++++++++++++++++++++++++++++++++
 fhem/docs/commandref.html  |  88 +++++++++-
 2 files changed, 431 insertions(+), 1 deletion(-)
 create mode 100755 fhem/FHEM/71_YAMAHA_AVR.pm

diff --git a/fhem/FHEM/71_YAMAHA_AVR.pm b/fhem/FHEM/71_YAMAHA_AVR.pm
new file mode 100755
index 000000000..c903195c9
--- /dev/null
+++ b/fhem/FHEM/71_YAMAHA_AVR.pm
@@ -0,0 +1,344 @@
+#
+#  Module: YAMAHA_AVR
+#
+# An FHEM Perl module for controlling Yamaha AV-Receivers
+# via network connection. As the interface is standardized
+# within all Yamaha AV-Receivers, this module should work
+# with any receiver which has an ethernet or wlan connection.
+# 
+# Currently supported are:  power (on|off)
+#                           input (hdmi1|hdmi2|...)
+#                           volume (-50 ... 10)
+#                           mute (on|off)
+#
+# Of course there are more possibilities than these 4 commands.
+# But in my oppinion these are the most relevant usecases within FHEM.
+#
+# For more commands, please email me.
+#
+# Written by Notausstieg (Notausstieg0309[at]googlemail[dot]com)
+#
+# 29.10.2012
+#
+###################################
+
+package main;
+
+use strict;
+use warnings;
+#use Time::HiRes qw(gettimeofday);
+
+
+sub YAMAHA_AVR_Get($@);
+sub YAMAHA_AVR_Define($$);
+sub YAMAHA_AVR_GetStatus($);
+
+
+
+
+###################################
+sub
+YAMAHA_AVR_Initialize($)
+{
+  my ($hash) = @_;
+
+  $hash->{GetFn}     = "YAMAHA_AVR_Get";
+  $hash->{SetFn}     = "YAMAHA_AVR_Set";
+  $hash->{DefFn}     = "YAMAHA_AVR_Define";
+  $hash->{UndefFn}   = "YAMAHA_AVR_Undefine";
+
+  $hash->{AttrList}  = "loglevel:0,1,2,3,4,5 subType event-on-update-reading event-on-change-reading";
+}
+
+###################################
+sub
+YAMAHA_AVR_GetStatus($)
+{
+    my ($hash) = @_;
+    my $name = $hash->{NAME};
+    my $power;
+    
+    return "" if(!defined($hash->{ADDRESS}) or !defined($hash->{INTERVAL}));
+    
+    my $device = $hash->{ADDRESS};
+    my $return = SendCommand($device,"<YAMAHA_AV cmd=\"GET\"><Main_Zone><Basic_Status>GetParam</Basic_Status></Main_Zone></YAMAHA_AV>");
+
+
+    return "Can't submit command. please see fhem logfile for further information" if(not defined($return) or length($return) == 0);
+    
+    readingsBeginUpdate($hash);
+    
+    if($return =~ /<Power>(.+)<\/Power>/)
+    {
+       $power = $1;
+       readingsUpdate($hash, "power", lc($power));
+       if($power eq "Standby")
+       {
+	    $power = "Off";
+       }
+       
+       $hash->{STATE} = lc($power);
+       
+    }
+    if($return =~ /<Volume><Lvl><Val>(.+)<\/Val><Exp>(.+)<\/Exp><Unit>.+<\/Unit><\/Lvl><Mute>(.+)<\/Mute><\/Volume>/)
+    {
+	readingsUpdate($hash, "volume_level", ($1 / 10 ** $2));
+	readingsUpdate($hash, "mute", lc($3));
+    }
+    
+    if($return =~ /<Input_Sel>(.+)<\/Input_Sel>/)
+    {
+	readingsUpdate($hash, "input", lc($1));
+    }
+    
+    readingsEndUpdate($hash, 1);
+    
+    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "YAMAHA_AVR_GetStatus", $hash, 1);
+    
+    Log GetLogLevel($name,4), "YAMAHA_AVR $name: $hash->{STATE}";
+    
+    return $hash->{STATE};
+}
+
+###################################
+sub
+YAMAHA_AVR_Get($@)
+{
+    my ($hash, @a) = @_;
+    my $what;
+
+    return "argument is missing" if(int(@a) != 2);
+    $what = $a[1];
+    
+    
+    if($what =~ /^(power|input|volume|mute)$/)
+    {
+        YAMAHA_AVR_GetStatus($hash);
+        if(defined($hash->{READINGS}{$what}))
+        {
+    	    return $a[0]." ".$what." => ".$hash->{READINGS}{$what}{VAL};
+	}
+	else
+	{
+	    return "no such reading: $what";
+	}
+    }
+    else
+    {
+	return "Unknown argument $what, choose one of param power input volume mute get";
+    }
+}
+
+
+###################################
+sub
+YAMAHA_AVR_Set($@)
+{
+    my ($hash, @a) = @_;
+    my $name = $hash->{NAME};
+    my $address = $hash->{ADDRESS};
+    my $result = "";
+    my $inputs_piped = $hash->{INPUTS};
+    
+    return "No Argument given" if(!defined($a[1]));
+    
+    my $what = $a[1];
+    my $usage = "Unknown argument $what, choose one of on off volume:slider,-80,1,16 input:".$hash->{INPUTS}." mute:on,off statusRequest";
+
+    readingsBeginUpdate($hash);
+    if($what eq "on")
+    {
+	$result = SendCommand($address, "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Power_Control><Power>On</Power></Power_Control></Main_Zone></YAMAHA_AV>");
+	if($result =~ /RC="0"/ and $result =~ /<Power><\/Power>/)
+	{
+	    # As the receiver startup takes about 5 seconds, the status will be already set, if the return code of the command is 0.
+	    readingsUpdate($hash, "power", "on");
+	    $hash->{STATE} = "on";
+	    return undef;
+	}   
+    }
+    elsif($what eq "off")
+    {
+	SendCommand($address, "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Power_Control><Power>Standby</Power></Power_Control></Main_Zone></YAMAHA_AV>");
+    }
+    elsif($what eq "input")
+    {
+	if(defined($a[2]))
+	{
+	    if($hash->{STATE} eq "on")
+	    {
+	        $inputs_piped =~ s/,/|/g;
+	        
+		if($a[2] =~ /^($inputs_piped)$/)
+		{
+		    if($a[2] eq "netradio")
+		    {
+			$result = SendCommand($address,"<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Input><Input_Sel>NET RADIO</Input_Sel></Input></Main_Zone></YAMAHA_AV>");
+		    }
+		    elsif($a[2] eq "airplay")
+		    {
+			$result = SendCommand($address,"<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Input><Input_Sel>AirPlay</Input_Sel></Input></Main_Zone></YAMAHA_AV>");
+		    }
+		    else
+		    {
+			$result = SendCommand($address,"<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Input><Input_Sel>".uc($a[2])."</Input_Sel></Input></Main_Zone></YAMAHA_AV>");
+		    }
+
+		    if(not $result =~ /RC="0"/)
+		    {
+			# if the returncode isn't 0, than the command was not successful
+			return "Could not set input to ".$a[2].". Please use only available inputs on your specific receiver";
+		    }
+		}
+		else
+		{
+		    return $usage;
+		}
+	    }
+	    else
+	    {
+		return "input can only be used when device is powered on";
+	    }
+	}
+    }
+    elsif($what eq "mute")
+    {
+	if(defined($a[2]))
+	{
+	    if($hash->{STATE} eq "on")
+	    {
+		if( $a[2] eq "on")
+		{
+		    SendCommand($address, "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Mute>On</Mute></Volume></Main_Zone></YAMAHA_AV>");
+		}
+		elsif($a[2] eq "off")
+		{
+		    SendCommand($address, "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Mute>Off</Mute></Volume></Main_Zone></YAMAHA_AV>"); 
+		
+		}
+		else
+		{
+		    return $usage;
+		}
+	    }
+	    else
+	    {
+		return "mute can only used when device is powered on";
+	    }
+	}
+    }
+    elsif($what eq "volume")
+    {
+	if(defined($a[2]) && $a[2] >= -80 && $a[2] < 16)
+	{
+	    if($hash->{STATE} eq "on")
+	    {
+		SendCommand($address,"<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Lvl><Val>".($a[2]*10)."</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>");
+	    }
+	    else
+	    {
+		return "volume can only be used when device is powered on";
+	    }
+	}
+    }
+    elsif($what eq "statusRequest")
+    {
+	# Will be executed on the end of this function anyway, so no need to call it specificly
+    }
+    else
+    {
+	return $usage;
+    }
+    readingsEndUpdate($hash, 1);
+    
+    YAMAHA_AVR_GetStatus($hash);
+    return undef;
+    
+}
+
+
+#############################
+sub
+YAMAHA_AVR_Define($$)
+{
+    my ($hash, $def) = @_;
+    my @a = split("[ \t][ \t]*", $def);
+    my $name = $hash->{NAME};
+    my @inputs;
+    
+    if(! @a >= 3)
+    {
+	my $msg = "wrong syntax: define <name> YAMAHA_AVR <ip-or-hostname> [<statusinterval>]";
+	Log 2, $msg;
+	return $msg;
+    }
+
+
+    my $address = $a[2];
+  
+    my $response = GetFileFromURL("http://".$address."/YamahaRemoteControl/desc.xml");
+    if($response =~ /<Unit_Description.* Unit_Name="(.+?)">/)
+    {
+        $hash->{MODEL} = $1;
+    }
+
+    $hash->{ADDRESS} = $address;
+  
+    $response = SendCommand($address, "<YAMAHA_AV cmd=\"GET\"><Main_Zone><Input><Input_Sel_Item>GetParam</Input_Sel_Item></Input></Main_Zone></YAMAHA_AV>");
+    $response =~ s/></>\n</g;
+    @inputs = split("\n", $response);
+    
+    foreach (sort @inputs)
+    {
+	if($_ =~ /<Param>(.+?)<\/Param>/ and not $1 =~ /iPod/)
+	{
+	    if(defined($hash->{INPUTS}) and length($hash->{INPUTS}) > 0)
+	    {
+	      $hash->{INPUTS} .= ",";
+	    }
+	    if($1 eq "NET RADIO")
+	    {
+		$hash->{INPUTS} .= "netradio";
+	    }
+	    else
+	    {
+		$hash->{INPUTS} .= lc($1);
+	    }
+	}
+    }
+    
+    
+    if(defined($a[3]) and $a[3] > 0)
+    {
+	$hash->{INTERVAL}=$a[3];
+    }
+    else
+    {
+	$hash->{INTERVAL}=30;
+    }
+
+    InternalTimer(gettimeofday()+2, "YAMAHA_AVR_GetStatus", $hash, 0);
+  
+  return undef;
+}
+
+#############################
+sub
+SendCommand($$)
+{
+   my($address, $command) = @_;
+   
+   # In case any URL changes must be made, this part is separated in this function".
+   return GetFileFromURL("http://".$address."/YamahaRemoteControl/ctrl", 10, "<?xml version=\"1.0\" encoding=\"utf-8\"?>".$command);
+}
+
+#############################
+sub
+YAMAHA_AVR_Undefine($$)
+{
+  my($hash, $name) = @_;
+  RemoveInternalTimer($hash);
+  return undef;
+}
+
+1;
diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html
index cd90e109c..5813ac432 100644
--- a/fhem/docs/commandref.html
+++ b/fhem/docs/commandref.html
@@ -136,7 +136,7 @@
       <a href="#SISPM">SISPM</a> &nbsp;
       <a href="#SIS_PMS">SIS_PMS</a> &nbsp;
       <a href="#SML">SML</a> &nbsp;
-	  <a href="#STV">STV</a> &nbsp;
+      <a href="#STV">STV</a> &nbsp;
       <a href="#TCM">TCM</a> &nbsp;
       <a href="#TellStick">TellStick</a> &nbsp;
       <a href="#TRX">TRX</a> &nbsp;
@@ -158,6 +158,7 @@
       <a href="#WS3600">WS3600</a> &nbsp;
       <a href="#X10">X10</a> &nbsp;
       <a href="#xxLG7000">xxLG7000</a> &nbsp;
+      <a href="#YAMAHA_AVR">YAMAHA_AVR</a> &nbsp;
       <a href="#ZWDongle">ZWDongle</a> &nbsp;
       <a href="#ZWave">ZWave</a> &nbsp;
 
@@ -10100,6 +10101,91 @@ KlikAanKlikUit, NEXA, CHACON, HomeEasy UK. <br> You need to define an RFXtrx433
   <br>
 </ul>
 
+<a name="YAMAHA_AVR"></a>
+<h3>YAMAHA_AVR</h3>
+<ul>
+
+  <a name="YAMAHA_AVRdefine"></a>
+  <b>Define</b>
+  <ul>
+    <code>define &lt;name&gt; YAMAHA_AVR &lt;ip-address&gt; [&lt;status_interval&gt;]</code>
+    <br><br>
+
+    This module controls AV receiver from Yamaha via network connection. You are able
+    to power your AV reveiver on and off, query it's power state,
+    select the input (HDMI, AV, AirPlay, internet radio, Tuner, ...), select the volume
+    or mute/unmute the volume.<br><br>
+    Defining a YAMAHA_AVR device will schedule an internal task (interval can be set
+    with optional parameter &lt;status_interval&gt; in seconds, if not set, the value is 60 seconds), which periodically reads
+    the status of the AV receiver (power state, selected input, volume and mute status)
+    and triggers notify/filelog commands.<br><br>
+
+    Example:
+    <ul>
+      <code>define AV_Receiver YAMAHA_AVR 192.168.0.10</code><br>
+    </ul>
+  <a name="YAMAHA_AVRset"></a>
+  <b>Set </b>
+  <ul>
+    <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code>
+    <br><br>
+    Currently, the following commands are defined; the available inputs are depending on the used receiver.
+    The module only offers the real available inputs. The following input commands are just an example and can differ.
+
+<pre>on
+off
+input hdmi1
+input hdmi2
+input hdmi3
+input hdmi4
+input av1
+input av2
+input av3
+input av3
+input av4
+input av5
+input av6
+input usb
+input airplay
+input tuner
+input v-aux
+input audio
+input server
+volume -80..16	(volume between -80 and +16 dB)
+mute on
+mute off</pre>
+  </ul>
+
+  <a name="YAMAHA_AVRget"></a>
+  <b>Get</b>
+  <ul>
+    <code>get &lt;name&gt; &lt;what&gt;</code>
+    <br><br>
+    Currently, the following commands are defined and return the current state of the receiver.
+<pre>power
+input 
+mute 
+volume</pre>
+  </ul>
+  <a name="YAMAHA_AVRattr"></a>
+  <b>Attributes</b>
+  <ul>
+  
+    <li><a href="#loglevel">loglevel</a></li>
+    <li><a href="#event-on-update-reading">event-on-update-reading</a></li>
+    <li><a href="#event-on-change-reading">event-on-change-reading</a></li>
+
+  </ul>
+<br>
+  <b>Implementator's note</b>
+  <ul>
+    The module is only usable if you activate "Network Standby" on your receiver.<br><br>
+    Technically there are many more commands and readings possible, but I think
+    these are the main usecases within FHEM.
+  </ul>
+  <br>
+</ul>
+
 <a name="ZWDongle"></a>
 <h3>ZWDongle</h3>
 <ul>