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> <a href="#SIS_PMS">SIS_PMS</a> <a href="#SML">SML</a> - <a href="#STV">STV</a> + <a href="#STV">STV</a> <a href="#TCM">TCM</a> <a href="#TellStick">TellStick</a> <a href="#TRX">TRX</a> @@ -158,6 +158,7 @@ <a href="#WS3600">WS3600</a> <a href="#X10">X10</a> <a href="#xxLG7000">xxLG7000</a> + <a href="#YAMAHA_AVR">YAMAHA_AVR</a> <a href="#ZWDongle">ZWDongle</a> <a href="#ZWave">ZWave</a> @@ -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 <name> YAMAHA_AVR <ip-address> [<status_interval>]</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 <status_interval> 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 <name> <command> [<parameter>]</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 <name> <what></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>