From ef21d3c7c8a69cd5750ba92bae50fe614ff72555 Mon Sep 17 00:00:00 2001 From: markusbloch <> Date: Fri, 16 Nov 2012 22:52:10 +0000 Subject: [PATCH] added zone feature (thanks to Fidel for testing and supporting) git-svn-id: https://svn.fhem.de/fhem/trunk@2128 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/71_YAMAHA_AVR.pm | 248 +++++++++++++++++++++++++++++++------ 1 file changed, 213 insertions(+), 35 deletions(-) diff --git a/fhem/FHEM/71_YAMAHA_AVR.pm b/fhem/FHEM/71_YAMAHA_AVR.pm index 73ab6b993..0189df6bf 100755 --- a/fhem/FHEM/71_YAMAHA_AVR.pm +++ b/fhem/FHEM/71_YAMAHA_AVR.pm @@ -72,6 +72,10 @@ YAMAHA_AVR_GetStatus($;$) my ($hash, $local) = @_; my $name = $hash->{NAME}; my $power; + #my $zone = YAMAHA_AVR_getZoneName($hash, AttrVal($name, "used-zone", "main")); + my $zone = YAMAHA_AVR_getZoneName($hash, $hash->{ACTIVE_ZONE}); + + $local = 0 unless(defined($local)); return "" if(!defined($hash->{helper}{ADDRESS}) or !defined($hash->{helper}{INTERVAL})); @@ -90,9 +94,10 @@ YAMAHA_AVR_GetStatus($;$) YAMAHA_AVR_getInputs($hash, $device); } + return "No Zone available" if(not defined($zone)); - my $return = YAMAHA_AVR_SendCommand($device,"GetParam"); + my $return = YAMAHA_AVR_SendCommand($device,"<$zone>GetParam"); Log GetLogLevel($name, 4), "YANMAHA_AVR: GetStatus-Request returned:\n$return"; @@ -119,12 +124,16 @@ YAMAHA_AVR_GetStatus($;$) } - if($return =~ /(.+)<\/Val>(.+)<\/Exp>.+<\/Unit><\/Lvl>(.+)<\/Mute><\/Volume>/) + if($return =~ /(.+)<\/Val>(.+)<\/Exp>.+<\/Unit><\/Lvl>(.+)<\/Mute>.*<\/Volume>/) { readingsBulkUpdate($hash, "volume_level", ($1 / 10 ** $2)); readingsBulkUpdate($hash, "mute", lc($3)); } + if($return =~ /.*?(.+?)<\/Output>.*?<\/Volume>/) + { + readingsBulkUpdate($hash, "output", lc($1)); + } if($return =~ /(.+)<\/Input_Sel>/) { @@ -180,7 +189,8 @@ YAMAHA_AVR_Set($@) my $address = $hash->{helper}{ADDRESS}; my $result = ""; my $command; - + #my $zone = YAMAHA_AVR_getZoneName($hash, AttrVal($name, "used-zone", "main")); + my $zone = YAMAHA_AVR_getZoneName($hash, $hash->{ACTIVE_ZONE}); my $inputs_piped = defined($hash->{helper}{INPUTS}) ? YAMAHA_AVR_InputParam2Fhem(lc($hash->{helper}{INPUTS}), 0) : "" ; my $inputs_comma = defined($hash->{helper}{INPUTS}) ? YAMAHA_AVR_InputParam2Fhem(lc($hash->{helper}{INPUTS}), 1) : "" ; @@ -194,7 +204,7 @@ YAMAHA_AVR_Set($@) if($what eq "on") { - $result = YAMAHA_AVR_SendCommand($address, "On"); + $result = YAMAHA_AVR_SendCommand($address, "<$zone>On"); if($result =~ /RC="0"/ and $result =~ /<\/Power>/) { @@ -202,12 +212,23 @@ YAMAHA_AVR_Set($@) readingsBulkUpdate($hash, "power", "on"); $hash->{STATE} = "on"; return undef; - } + } + else + { + return "Could not set power to on"; + } } elsif($what eq "off") { - YAMAHA_AVR_SendCommand($address, "Standby"); + $result = YAMAHA_AVR_SendCommand($address, "<$zone>Standby"); + + if(not $result =~ /RC="0"/) + { + # if the returncode isn't 0, than the command was not successful + return "Could not set power to off"; + } + } elsif($what eq "input") { @@ -223,7 +244,7 @@ YAMAHA_AVR_Set($@) $command = YAMAHA_AVR_getCommandParam($hash, $a[2]); if(defined($command) and length($command) > 0) { - $result = YAMAHA_AVR_SendCommand($address,"".$command.""); + $result = YAMAHA_AVR_SendCommand($address,"<$zone>".$command.""); } else { @@ -266,16 +287,22 @@ YAMAHA_AVR_Set($@) { if( $a[2] eq "on") { - YAMAHA_AVR_SendCommand($address, "On"); + $result = YAMAHA_AVR_SendCommand($address, "<$zone>On"); } elsif($a[2] eq "off") { - YAMAHA_AVR_SendCommand($address, "Off"); + $result = YAMAHA_AVR_SendCommand($address, "<$zone>Off"); } else { return $usage; } + + if(not $result =~ /RC="0"/) + { + # if the returncode isn't 0, than the command was not successful + return "Could not set mute to ".$a[2]."."; + } } else { @@ -299,29 +326,35 @@ YAMAHA_AVR_Set($@) if($diff > 0) { - Log GetLogLevel($name, 4), "YAMAHA_AV: use smooth volume change (with $steps steps of +$diff volume change each ".sprintf("%.3f", $sleep)." seconds)"; + Log GetLogLevel($name, 4), "YAMAHA_AVR: use smooth volume change (with $steps steps of +$diff volume change each ".sprintf("%.3f", $sleep)." seconds)"; } else { - Log GetLogLevel($name, 4), "YAMAHA_AV: use smooth volume change (with $steps steps of $diff volume change each ".sprintf("%.3f", $sleep)." seconds)"; + Log GetLogLevel($name, 4), "YAMAHA_AVR: use smooth volume change (with $steps steps of $diff volume change each ".sprintf("%.3f", $sleep)." seconds)"; } # Only if smoohing is really needed (step difference is not zero) - if($diff != 0) + if(defined($hash->{READINGS}{volume_level}{VAL}) and $diff != 0) { for(my $step = 1; $step <= $steps; $step++) { - Log GetLogLevel($name, 4), "YAMAHA_AV: set volume to ".($current_volume + ($diff * $step))." dB"; + Log GetLogLevel($name, 4), "YAMAHA_AVR: set volume to ".($current_volume + ($diff * $step))." dB"; - YAMAHA_AVR_SendCommand($address,"".(($current_volume + ($diff * $step))*10)."1dB"); + YAMAHA_AVR_SendCommand($address,"<$zone>".(($current_volume + ($diff * $step))*10)."1dB"); sleep $sleep unless ($time == 0); } } } - Log GetLogLevel($name, 4), "YAMAHA_AV set volume to ".$a[2]." dB"; - YAMAHA_AVR_SendCommand($address,"".($a[2]*10)."1dB"); + Log GetLogLevel($name, 4), "YAMAHA_AVR: set volume to ".$a[2]." dB"; + $result = YAMAHA_AVR_SendCommand($address,"<$zone>".($a[2]*10)."1dB"); + if(not $result =~ /RC="0"/) + { + # if the returncode isn't 0, than the command was not successful + return "Could not set volume to ".$a[2]."."; + } + } else { @@ -353,9 +386,9 @@ YAMAHA_AVR_Define($$) my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; - if(! @a >= 3) + if(! @a >= 4) { - my $msg = "wrong syntax: define YAMAHA_AVR []"; + my $msg = "wrong syntax: define YAMAHA_AVR [] []"; Log 2, $msg; return $msg; } @@ -366,15 +399,47 @@ YAMAHA_AVR_Define($$) $hash->{helper}{ADDRESS} = $address; - if(defined($a[3]) and $a[3] > 0) + + if(defined($a[3])) { - $hash->{helper}{INTERVAL}=$a[3]; + $hash->{helper}{SELECTED_ZONE} = $a[3]; + } + else + { + $hash->{helper}{SELECTED_ZONE} = "mainzone"; + } + + if(defined($a[4]) and $a[4] > 0) + { + $hash->{helper}{INTERVAL}=$a[4]; } else { $hash->{helper}{INTERVAL}=30; } - $attr{$name}{"volume-smooth-change"} = "1"; + + + # In case of a redefine, check the zone parameter if the specified zone exist, otherwise use the main zone + if(defined($hash->{helper}{ZONES}) and length($hash->{helper}{ZONES}) > 0) + { + if(defined(YAMAHA_AVR_getZoneName($hash, lc $hash->{helper}{SELECTED_ZONE}))) + { + + $hash->{ACTIVE_ZONE} = lc $hash->{helper}{SELECTED_ZONE}; + YAMAHA_AVR_getInputs($hash, $address); + + } + else + { + Log GetLogLevel($name, 2), "YAMAHA_AVR: selected zone >>".$hash->{helper}{SELECTED_ZONE}."<< is not available on device ".$hash->{NAME}.". Using Main Zone instead"; + $hash->{ACTIVE_ZONE} = "mainzone"; + YAMAHA_AVR_getInputs($hash, $address); + } + } + + + + $attr{$name}{"volume-smooth-change"} = "1" unless(defined($attr{$name}{"volume-smooth-change"})); InternalTimer(gettimeofday()+2, "YAMAHA_AVR_GetStatus", $hash, 0); @@ -410,12 +475,52 @@ sub YAMAHA_AVR_InputParam2Fhem($$) $inputs =~ s/\s+//g; $inputs =~ s/,//g; - $inputs =~ s/\(.+?\)//g; + $inputs =~ s/\(/_/g; + $inputs =~ s/\)//g; $inputs =~ s/\|/,/g if($replace_pipes == 1); return $inputs; } +############################# +# Converts all Zones to FHEM usable command lists +sub YAMAHA_AVR_Zone2Fhem($$) +{ + my ($zones, $replace_pipes) = @_; + + + $zones =~ s/\s+//g; + $zones =~ s/_//g; + $zones =~ s/\|/,/g if($replace_pipes == 1); + + return lc $zones; + +} + +############################# +# Returns the Yamaha Zone Name for the FHEM like zone attribute +sub YAMAHA_AVR_getZoneName($$) +{ + my ($hash, $zone) = @_; + my $item; + + return undef if(not defined($hash->{helper}{ZONES})); + + my @commands = split("\\|", $hash->{helper}{ZONES}); + + foreach $item (@commands) + { + if(YAMAHA_AVR_Zone2Fhem($item, 0) eq $zone) + { + return $item; + } + + } + + return undef; + +} + ############################# # Returns the Yamaha Parameter Name for the FHEM like input channel sub YAMAHA_AVR_getCommandParam($$) @@ -441,6 +546,7 @@ sub YAMAHA_AVR_getCommandParam($$) sub YAMAHA_AVR_getModel($$) { my ($hash, $address) = @_; + my $name = $hash->{NAME}; my $response = GetFileFromURL("http://".$address."/YamahaRemoteControl/desc.xml"); return undef unless(defined($response)); if($response =~ //) @@ -448,20 +554,55 @@ sub YAMAHA_AVR_getModel($$) $hash->{FIRMWARE} = $1; $hash->{MODEL} = $2; } + + + while($response =~ //gc) + { + if(defined($hash->{helper}{ZONES}) and length($hash->{helper}{ZONES}) > 0) + { + $hash->{helper}{ZONES} .= "|"; + } + + $hash->{helper}{ZONES} .= $2; + + } + + # uncommented line for zone detection testing + # + # $hash->{helper}{ZONES} .= "|Zone_2"; + + $hash->{ZONES_AVAILABLE} = YAMAHA_AVR_Zone2Fhem($hash->{helper}{ZONES}, 1); + + + if(defined(YAMAHA_AVR_getZoneName($hash, lc $hash->{helper}{SELECTED_ZONE}))) + { + + Log GetLogLevel($name, 4), "YAMAHA_AVR: using zone ".YAMAHA_AVR_getZoneName($hash, lc $hash->{helper}{SELECTED_ZONE}); + $hash->{ACTIVE_ZONE} = lc $hash->{helper}{SELECTED_ZONE}; + + } + else + { + Log GetLogLevel($name, 2), "YAMAHA_AVR: selected zone >>".$hash->{helper}{SELECTED_ZONE}."<< is not available on device $name. Using Main Zone instead"; + $hash->{ACTIVE_ZONE} = "mainzone"; + } } sub YAMAHA_AVR_getInputs($$) { my ($hash, $address) = @_; - my $response = YAMAHA_AVR_SendCommand($address, "GetParam"); + my $name = $hash->{NAME}; + my $zone = YAMAHA_AVR_getZoneName($hash, $hash->{ACTIVE_ZONE}); + my $response = YAMAHA_AVR_SendCommand($address, "<$zone>GetParam"); return undef unless (defined($response)); - $response =~ s/>\n(.+?)<\/Param>/) + undef($hash->{helper}{INPUTS}) if(defined($hash->{helper}{INPUTS})); + + + + while($response =~ /(.+?)<\/Param>/gc) { if(defined($hash->{helper}{INPUTS}) and length($hash->{helper}{INPUTS}) > 0) { @@ -471,7 +612,10 @@ sub YAMAHA_AVR_getInputs($$) $hash->{helper}{INPUTS} .= $1; } - } + + $hash->{helper}{INPUTS} = join("|", sort split("\\|", $hash->{helper}{INPUTS})); + + } @@ -489,7 +633,7 @@ sub YAMAHA_AVR_getInputs($$) Define
    - define <name> YAMAHA_AVR <ip-address> [<status_interval>] + define <name> YAMAHA_AVR <ip-address> [<zone>] [<status_interval>]

    This module controls AV receiver from Yamaha via network connection. You are able @@ -497,14 +641,48 @@ sub YAMAHA_AVR_getInputs($$) select the input (HDMI, AV, AirPlay, internet radio, Tuner, ...), select the volume or mute/unmute the volume.

    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 30 seconds), which periodically reads - the status of the AV receiver (power state, selected input, volume and mute status) - and triggers notify/filelog commands.

    + with optional parameter <status_interval> in seconds, if not set, the value is 30 + seconds), which periodically reads the status of the AV receiver (power state, selected + input, volume and mute status) and triggers notify/filelog commands.

    Example: +
    +       define AV_Receiver YAMAHA_AVR 192.168.0.10
    +       
    +       define AV_Receiver YAMAHA_AVR 192.168.0.10 mainzone 60   # With custom interval of 60 seconds
    +    
    +
+ Zone Selection
+
    + If your receiver supports Zone Selection (e.g. RX-V671, RX_V673,... and the AVANTAGE series) + you can select the zone which should be controlled. The RX_4xx and RX_3xx series for example + just have a "Main Zone" (which is the whole receiver itself). In general you have the following + possibilities for the parameter <zone> (depending on your receiver model).

      - define AV_Receiver YAMAHA_AVR 192.168.0.10
      +
    • mainzone - this is the main zone (standard)
    • +
    • zone2 - The second zone (Zone 2)
    • +
    • zone3 - The third zone (Zone 3)
    • +
    • zone4 - The fourth zone (Zone 4)
    +
    + Depending on your receiver model you have not all inputs available on these different zones. + The module just offers the real available inputs. +

    + Example: + +
    +        define AV_Receiver YAMAHA_AVR 192.168.0.10           # If no zone is specified, the "Main Zone" will be used.
    +        attr AV_Receiver YAMAHA_AVR room Livingroom
    +        
    +        # Define the second zone
    +        define AV_Receiver_Zone2 YAMAHA_AVR 192.168.0.10 zone2
    +        attr AV_Receiver_Zone2 room Bedroom
    +     
    + Each Zone needs an own device and can be assigned to the different rooms, as each zone + can be separatly controlled from the other zones. +

    +
+ Set
    @@ -576,7 +754,7 @@ volume_level

- + =end html =cut