############################################################################### # 71_DENON_AVR_ZONE # # 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 . # ############################################################################### # # DENON_AVR_ZONE maintained by Martin Gutenbrunner # original credits to raman and the community (see Forum thread) # # This module enables FHEM to interact with Denon and Marantz audio devices. # # Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,58452.300.html # # $Id$ package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); sub DENON_AVR_ZONE_Get($$$); sub DENON_AVR_ZONE_Set($$$); sub DENON_AVR_ZONE_Attr($@); sub DENON_AVR_ZONE_Define($$$); sub DENON_AVR_ZONE_Undefine($$); # Database and call-functions ###################################################################### my $DENON_db_zone = { 'UP' => 'up', 'DOWN' => 'down', 'ON' => 'on', 'OFF' => 'off', 'PHONO' => 'Phono', 'CD' => 'CD', 'TUNER' => 'Tuner', 'DVD' => 'DVD', 'BD' => 'Blu-Ray', 'TV' => 'TV', 'SAT/CBL' => 'Cbl/Sat', 'MPLAY' => 'Mediaplayer', 'GAME' => 'Game', 'HDRADIO' => 'HDRadio', 'NET' => 'OnlineMusic', 'SPOTIFY' => 'Spotify', 'LASTFM' => 'LastFM', 'FLICKR' => 'Flickr', 'IRADIO' => 'iRadio', 'SERVER' => 'Server', 'FAVORITES' => 'Favorites', 'PANDORA' => 'Pandora', 'SIRIUSXM' => 'SiriusXM', 'AUX1' => 'Aux1', 'AUX2' => 'Aux2', 'AUX3' => 'Aux3', 'AUX4' => 'Aux4', 'AUX5' => 'Aux5', 'AUX6' => 'Aux6', 'AUX7' => 'Aux7', 'BT' => 'Bluetooth', 'USB/IPOD' => 'Usb/iPod', 'USB' => 'Usb_play', 'IPD' => 'iPod_play', 'IRP' => 'iRadio_play', 'FVP' => 'Favorites_play', 'QUICK1' => 'Quick1', 'QUICK2' => 'Quick2', 'QUICK3' => 'Quick2', 'QUICK4' => 'Quick4', 'QUICK5' => 'Quick5', 'SMART1' => 'Smart1', 'SMART2' => 'Smart2', 'SMART3' => 'Smart3', 'SMART4' => 'Smart4', 'SMART5' => 'Smart5', 'SI' => { 'Phono' => 'PHONO', 'CD' => 'CD', 'Tuner' => 'TUNER', 'DVD' => 'DVD', 'Blu-Ray' => 'BD', 'TV' => 'TV', 'Cbl/Sat' => 'SAT/CBL', 'Mediaplayer' => 'MPLAY', 'Game' => 'GAME', 'HDRadio' => 'HDRADIO', 'OnlineMusic' => 'NET', 'Spotify' => 'SPOTIFY', 'LastFM' => 'LASTFM', 'Flickr' => 'FLICKR', 'iRadio' => 'IRADIO', 'Server' => 'SERVER', 'Favorites' => 'FAVORITES', 'Pandora' => 'PANDORA', 'SiriusXM' => 'SIRIUSXM', 'Aux1' => 'AUX1', 'Aux2' => 'AUX2', 'Aux3' => 'AUX3', 'Aux4' => 'AUX4', 'Aux5' => 'AUX5', 'Aux6' => 'AUX6', 'Aux7' => 'AUX7', 'Bluetooth' => 'BT', 'Usb/iPod' => 'USB/IPOD', 'Usb_play' => 'USB', 'iPod_play' => 'IPD', 'iRadio_play' => 'IRP', 'Favorites_play' => 'FVP', 'Source' => 'SOURCE', # 'Status' => '?', }, 'MU' => { 'on' => 'ON', 'off' => 'OFF', 'status' => '?', }, 'CS' => { 'stereo' => 'ST', 'mono' => 'MONO', 'status' => '?', }, 'CV' => { 'FL' => 'FrontLeft', 'FR' => 'FrontRight', }, 'HPF' => { 'on' => 'ON', 'off' => 'OFF', 'status' => '?', }, 'PS' => { 'BAS' => 'bass', 'TRE' => 'treble', }, 'HDA' => { 'HDA' => 'HDMI_out', 'THR' => 'through', 'PCM' => 'pcm', }, 'SLP' => { 'off' => 'OFF', '10min' => '010', '15min' => '015', '30min' => '030', '40min' => '040', '50min' => '050', '60min' => '060', '70min' => '070', '80min' => '080', '90min' => '090', '100min' => '100', '110min' => '110', '120min' => '120', 'status' => '?', }, 'STBY' => { '2h' => '2H', '4h' => '4H', '8h' => '8H', 'off' => 'OFF', 'status' => '?', }, 'SWITCH' => { "on" => "off", "off" => "on", }, 'REMOTE' => { #Remote - all commands: 'up' => 'UP', 'down' => 'DOWN', }, 'SOD' => { # used inputs: USE = aviable / DEL = not aviable 'DVD' => 'USE', 'BD' => 'USE', 'TV' => 'USE', 'SAT/CBL' => 'USE', 'SAT' => 'DEL', 'MPLAY' => 'USE', 'BT' => 'USE', 'GAME' => 'USE', 'HDRADIO' => 'DEL', 'AUX1' => 'USE', 'AUX2' => 'USE', 'AUX3' => 'DEL', 'AUX4' => 'DEL', 'AUX5' => 'DEL', 'AUX6' => 'DEL', 'AUX7' => 'DEL', 'AUXA' => 'DEL', 'AUXB' => 'DEL', 'AUXC' => 'DEL', 'AUXD' => 'DEL', 'CD' => 'USE', 'PHONO' => 'USE', 'TUNER' => 'USE', 'FAVORITES' => 'USE', 'IRADIO' => 'USE', 'SIRIUSXM' => 'DEL', 'PANDORA' => 'DEL', 'SERVER' => 'USE', 'FLICKR' => 'USE', 'NET' => 'USE', 'LASTFM' => 'DEL', 'USB/IPOD' => 'USE', 'USB' => 'USE', 'IPD' => 'USE', 'IRP' => 'USE', 'FVP' => 'USE', 'SOURCE' => 'USE', }, }; sub DENON_ZONE_GetValue($;$;$) { my ( $status, $com, $inf) = @_; my $command = (defined($com) ? $com : "na"); my $info = (defined($inf) ? $inf : "na"); if ( $command eq "na" && $info eq "na" && defined( $DENON_db_zone->{$status} ) ) { my $value = eval { $DENON_db_zone->{$status} }; $value = $@ ? "unknown" : $value; return $value; } elsif ( defined($DENON_db_zone->{$status}{$command} ) && $info eq "na" ) { my $value = eval { $DENON_db_zone->{$status}{$command} }; $value = $@ ? "unknown" : $value; return $value; } elsif ( defined($DENON_db_zone->{$status}{$command}{$info}) ) { my $value = eval { $DENON_db_zone->{$status}{$command}{$info} }; $value = $@ ? "unknown" : $value; return $value; } else { return "unknown"; } } sub DENON_ZONE_GetKey($$;$) { my ( $status, $command, $info) = @_; if ( defined($status) && defined($command) && !defined($info)) { my @keys = keys %{$DENON_db_zone->{$status}}; my @values = values %{$DENON_db_zone->{$status}}; while (@keys) { my $fhemCommand = pop(@keys); my $denonCommand = pop(@values); if ($command eq $denonCommand) { return $fhemCommand; } } } if ( defined($status) && defined($command) && defined($info)) { my @keys = keys %{$DENON_db_zone->{$status}{$command}}; my @values = values %{$DENON_db_zone->{$status}{$command}}; while (@keys) { my $fhemCommand = pop(@keys); my $denonCommand = pop(@values); if ($info eq $denonCommand) { return $fhemCommand; } } } else { return undef; } } ################################### sub DENON_AVR_ZONE_Initialize($) { my ($hash) = @_; $hash->{Match} = ".+"; $hash->{GetFn} = "DENON_AVR_ZONE_Get"; $hash->{SetFn} = "DENON_AVR_ZONE_Set"; $hash->{DefFn} = "DENON_AVR_ZONE_Define"; $hash->{UndefFn} = "DENON_AVR_ZONE_Undefine"; $hash->{ParseFn} = "DENON_AVR_ZONE_Parse"; $hash->{AttrFn} = "DENON_AVR_ZONE_Attr"; $hash->{AttrList} = "IODev brand:Denon,Marantz do_not_notify:1,0 disable:0,1 connectionCheck:off,30,45,60,75,90,105,120,240,300 timeout:1,2,3,4,5 unit:off,on ".$readingFnAttributes; $data{RC_makenotify}{DENON_AVR_ZONE} = "DENON_AVR_ZONE_RCmakenotify"; $data{RC_layout}{DENON_AVR_ZONE_RC} = "DENON_AVR_ZONE_RClayout"; $hash->{parseParams} = 1; } ############################# sub DENON_AVR_ZONE_Define($$$) { my ($hash, $a, $h) = @_; my $name = $hash->{NAME}; return "Usage: define DENON_AVR_ZONE ... wrong paramter count: ".int(@$a) if(int(@$a) != 4); AssignIoPort($hash, @$a[3]); my $IOhash = $hash->{IODev}; my $IOname = $IOhash->{NAME}; my $zone; if(!defined($IOhash) && !defined($IOname)) { my $err= "DENON_AVR_ZONE $name error: no I/O device."; Log3 $hash, 1, $err; return $err; } if ( !defined( @$a[2] ) ) { $zone = "2"; } elsif ( @$a[2] eq "2" || @$a[2] eq "3" || @$a[2] eq "4") { $zone = @$a[2]; } else { return @$a[2] . " is not a valid Zone number"; } if ( defined($modules{DENON_AVR_ZONE}{defptr}{$IOname}{$zone}) ) { return "Zone already defined in " . $modules{DENON_AVR_ZONE}{defptr}{$IOname}{$zone}{NAME}; } if ( !defined($IOhash) ) { return "No matching I/O device found, please define a DENON_AVR device first"; } elsif ( !defined( $IOhash->{TYPE} ) || !defined( $IOhash->{NAME} ) ) { return "IODev does not seem to be existing"; } elsif ( $IOhash->{TYPE} ne "DENON_AVR" ) { return "IODev is not of type DENON_AVR"; } else { $hash->{ZONE} = $zone; $modules{DENON_AVR_ZONE}{defptr}{$IOname}{$zone} = $hash; } # set default attributes unless ( exists( $attr{$name}{webCmd} ) ) { $attr{$name}{webCmd} = 'volume:muteT:input'; } unless ( exists( $attr{$name}{cmdIcon} ) ) { $attr{$name}{cmdIcon} = 'muteT:rc_MUTE'; } unless ( exists( $attr{$name}{devStateIcon} ) ) { $attr{$name}{devStateIcon} = 'on:rc_GREEN:off off:rc_STOP:on absent:rc_RED muted:rc_MUTE@green:muteT'; } unless (exists($attr{$name}{stateFormat})){ $attr{$name}{stateFormat} = 'stateAV'; } return undef; } ##################################### sub DENON_AVR_ZONE_Undefine($$) { my ( $hash, $name ) = @_; my $zone = $hash->{ZONE}; my $IOhash = $hash->{IODev}; my $IOname = $IOhash->{NAME}; Log3 $name, 5, "DENON_AVR_ZONE $name: called function DENON_AVR_ZONE_Undefine()"; delete $modules{DENON_AVR_ZONE}{defptr}{$IOname}{$zone} if ( defined( $modules{DENON_AVR_ZONE}{defptr}{$IOname}{$zone} ) ); DENON_AVR_ZONE_RCdelete($name); # Disconnect from device DevIo_CloseDev($hash); # Stop the internal GetStatus-Loop and exit RemoveInternalTimer($hash); return undef; } ##################################### sub DENON_AVR_ZONE_Parse(@) { my ($IOhash, $msg) = @_; # IOhash points to the DENON_AVR, not to the DENON_AVR_ZONE my @matches; my $name = $IOhash->{NAME}; foreach my $d (keys %defs) { my $hash = $defs{$d}; my $state = ReadingsVal( $name, "power", "off" ); if($hash->{TYPE} eq "DENON_AVR_ZONE" && $hash->{IODev} eq $IOhash) { my $return = "unknown"; my $zone = 2; my $zonehash = undef; my $zonename = undef; if ( defined($modules{DENON_AVR_ZONE}{defptr}{$name}{2}) && $msg =~ /^Z2/ ) { $zone = 2; $zonehash = $modules{DENON_AVR_ZONE}{defptr}{$name}{2}; $zonename = $zonehash->{NAME}; } elsif ( defined($modules{DENON_AVR_ZONE}{defptr}{$name}{3}) && $msg =~ /^Z3/ ) { $zone = 3; $zonehash = $modules{DENON_AVR_ZONE}{defptr}{$name}{3}; $zonename = $zonehash->{NAME}; } elsif ( defined($modules{DENON_AVR_ZONE}{defptr}{$name}{4}) && $msg =~ /^Z4/ ) { $zone = 4; $zonehash = $modules{DENON_AVR_ZONE}{defptr}{$name}{4}; $zonename = $zonehash->{NAME}; } my $dezibel = AttrVal($zonename, "unit", "off") eq "on" ? " dB" : ""; my $percent = AttrVal($zonename, "unit", "off") eq "on" ? " %" : ""; Log3 $zonename, 5, "DENON_AVR_ZONE $zone: $name "; push @matches, $d; if ($msg =~ /^(Z2|Z3|Z4)(.+)/) { my $zone = $1; my $arg = $2; readingsBeginUpdate($zonehash); if ($arg eq "ON" || $arg eq "OFF") { readingsBulkUpdate($zonehash, "power", lc($arg)); readingsBulkUpdate($zonehash, "state", lc($arg)); readingsBulkUpdate($zonehash, "presence", "present"); readingsBulkUpdate($zonehash, "stateAV", DENON_AVR_ZONE_GetStateAV($zonehash)); $return = lc($arg); } elsif ($arg =~ /^CS(.+)/) { Log3 $zonename, 0, "DENON_AVR_ZONE $zone: $name <$1>"; my $status = DENON_ZONE_GetKey("CS", $1); readingsBulkUpdate($zonehash, "channelSetting", $status); $return = "channelSetting " . $status; } elsif ($arg =~ /^CV([A-Z]+) (.+)/) { Log3 $zonename, 0, "DENON_AVR_ZONE $zone: $name <$1>"; my $channel = DENON_ZONE_GetValue("CV", $1); my $volume = $2; if (length($volume) == 2) { $volume = $volume."0"; } readingsBulkUpdate($zonehash, "level".$channel, ($volume / 10 - 50).$dezibel); $return = "level".$channel . " " . ($volume / 10 - 50).$dezibel; } elsif ($arg =~ /^FAVORITE(.+)/) { readingsBulkUpdate($zonehash, "favorite", $1); $return = "favorite " . $1; } elsif ($arg =~ /^MU(.+)/) { readingsBulkUpdate($zonehash, "mute", lc($1)); readingsBulkUpdate($zonehash, "stateAV", DENON_AVR_ZONE_GetStateAV($zonehash)); $return = "mute " . lc($1); } elsif ($arg =~ /^HDA(.+)/) { my $status = DENON_ZONE_GetValue('HDA', $1); readingsBulkUpdate($zonehash, "digitalOut", $status); $return = "digitalOut " . $status; } elsif ($arg =~ /^HPF(.+)/) { readingsBulkUpdate($zonehash, "highPassFilter", lc($1)); $return = "highPassFilter " . lc($1); } elsif ($arg =~/^PS(.+)/) { my $parameter = $1; if($parameter =~ /^([A-Z]{3}) (.+)/) { my $status = DENON_ZONE_GetValue('PS', $1); my $volume = $2; if (length($volume) == 2) { $volume = $volume."0"; } $volume = ($volume / 10 - 50).$dezibel; readingsBulkUpdate($zonehash, $status, $volume); $return = $status . " " . $volume; } } elsif ($arg =~ /^QUICK(.+)/) { readingsBulkUpdate($zonehash, "quickselect", $1); $return = "quickselect " . $1; } elsif ($arg =~ /^SMART(.+)/) { readingsBulkUpdate($zonehash, "smartselect", $1); $return = "smartselect " . $1; } elsif ($arg =~ /^SLP(.+)/) { readingsBulkUpdate($zonehash, "sleep", $1."min"); $return = "sleep " . $1."min"; } elsif ($arg =~ /^STBY(.+)/) { my $status = DENON_ZONE_GetKey("STBY", $1); readingsBulkUpdate($zonehash, "autoStandby", $status); $return = "autoStandby " . $status; } elsif ($arg =~ /(^[0-9]+)/){ my $volume = $1; if (length($volume) == 2) { $volume = $volume."0"; } readingsBulkUpdate($zonehash, "volumeStraight", ($volume / 10 - 80).$percent); readingsBulkUpdate($zonehash, "volume", ($volume / 10).$dezibel); $return = "volume/volumeStraight ".($volume / 10)."/".($volume / 10 - 80); } else{ if ($arg eq 'SOURCE'){ my $status = ReadingsVal($name, "input", "Cbl/Sat"); readingsBulkUpdate($zonehash, "input", $status); $return = "input " . $status; } else{ my $status = DENON_ZONE_GetValue($arg); readingsBulkUpdate($zonehash, "input", $status); $return = "input " . $status; } } readingsEndUpdate($zonehash, 1); } elsif ( $msg =~ /^power (.+)/ && defined($zonehash) ) { readingsBeginUpdate($zonehash); readingsBulkUpdate($zonehash, "power", $1); readingsEndUpdate($zonehash, 1); $return = $1; } elsif ( $msg =~ /^presence (.+)/ && defined($zonehash) ) { readingsBeginUpdate($zonehash); readingsBulkUpdate($zonehash, "presence", $1); readingsEndUpdate($zonehash, 1); $return = "presence " . $1; } Log3 $zonename, 4, "DENON_AVR_ZONE $zone: parsing <$msg> to <$return>"; return @matches if (@matches); return "UNDEFINED DENON_AVR_ZONE"; } } } ##################################### sub DENON_AVR_ZONE_Get($$$) { my ( $hash, $a, $h ) = @_; my $arg; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; return "argument is missing" if (int(@$a) != 2); if (@$a[1] =~ /^(power|volumeStraight|volume|channelSetting|highPassFilter|mute|input|remotecontrol|autoStandby|sleep|zone|zoneStatusRequest)$/) { if (@$a[1] eq "remotecontrol") { return DENON_AVR_ZONE_RCmake($name); } elsif (@$a[1] eq "zoneStatusRequest") { DENON_AVR_ZONE_Command_Write($hash, "?", "power"); return "StatusRequest zone $zone finished!"; } elsif(defined(ReadingsVal( $name, @$a[1], "" ))) { return ReadingsVal( $name, @$a[1], "" ); } else { return "No such reading: @$a[1]"; } } else { return "Unknown argument @$a[1], choose one of power volumeStraight volume autoStandby channelSetting highPassFilter mute input mute remotecontrol sleep zone zoneStatusRequest"; } } ##################################### sub DENON_AVR_ZONE_Set($$$) { my ( $hash, $a, $h ) = @_; my $IOhash = $hash->{IODev}; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; my $state = ReadingsVal( $name, "power", "off" ); my $presence = ReadingsVal( $name, "presence", "absent" ); my $return; my $select = "quick"; my @channel = (); my @preset = (01..56); my @inputs = (); #my @usedInputs = (); my @remoteControl = (); my $dezibel = AttrVal($name, "unit", "off") eq "on" ? " dB" : ""; foreach my $key (sort(keys %{$DENON_db_zone->{'REMOTE'}})) { push(@remoteControl, $key); } if ( exists( $attr{$IOhash->{NAME}}{inputs} ) ) { @inputs = split(/,/,$attr{$IOhash->{NAME}}{inputs}); } else { foreach my $key (sort(keys %{$DENON_db_zone->{'SI'}})) { my $device = $DENON_db_zone->{'SI'}{$key}; if ( defined($DENON_db_zone->{'SOD'}{$device})) { if ($DENON_db_zone->{'SOD'}{$device} eq 'USE') { push(@inputs, $key); } #push(@usedInputs, $key); } } } if(AttrVal($name, "brand", "Denon") eq "Marantz") { $select = "smart"; } foreach my $key (sort(keys %{$DENON_db_zone->{'CV'}})) { push(@channel, $DENON_db_zone->{'CV'}{$key}."_up"); push(@channel, $DENON_db_zone->{'CV'}{$key}."_down"); } my $usage = "Unknown argument @$a[1], choose one of on off toggle volumeDown volumeUp volumeStraight:slider,-80,1,18 volume:slider,0,1,98 mute:on,off,toggle muteT sleep:off,10min,15min,20min,30min,40min,50min,60min,70min,80min,90min,100min,110min,120min autoStandby:off,2h,4h,8h channelSetting:stereo,mono highPassFilter:on,off " . "favorite:1,2,3,4" . " " . $select . "select:1,2,3,4,5 " . "input:" . join(",", @inputs) . " " . # "usedInputs:multiple-strict," . join(",", @usedInputs) . " " . "bass:slider,-10,1,10 treble:slider,-10,1,10 " . "channelVolume:" . join(",", @channel) . " " . "remoteControl:" . join(",", @remoteControl) . " " . "rawCommand"; if (@$a[1] eq "?") { $return = "?"; return $usage; } readingsBeginUpdate($hash); if (@$a[1] =~ /^(on|off)$/) { $return = DENON_AVR_ZONE_Command_SetPower($hash, @$a[1]); } elsif (@$a[1] eq "bass") { my $volume = @$a[2] + 50; $return = DENON_AVR_ZONE_Command_Write($hash, "PSBAS ".$volume, "bass", @$a[2].$dezibel); } elsif (@$a[1] eq "treble") { my $volume = @$a[2] + 50; $return = DENON_AVR_ZONE_Command_Write($hash, "PSTRE ".$volume, "treble", @$a[2].$dezibel); } elsif (@$a[1] eq "channelSetting") { my $favorite = @$a[2]; my $channel = DENON_ZONE_GetValue('CS', @$a[2]); $return = DENON_AVR_ZONE_Command_Write($hash, "CS".$channel, "channelSetting", @$a[2]); } elsif (@$a[1] eq "channelVolume") { my $channel = ""; my $command = @$a[2]; my $volume = ""; if($command =~ /^(.+)_(up|down)/) { $channel = DENON_ZONE_GetKey("CV", $1); $channel = $channel." ".uc($2); #my $state = ReadingsVal( $name, $channel, "0" ); #$volume = uc($2); $volume = "query_CV?"; } else { $channel = DENON_ZONE_GetKey("CV", $command); $volume = @$a[3] + 50; if ($volume % 1 == 0) { $volume = 40 if($volume < 40); $volume = 60 if($volume > 60); $volume = sprintf ('%02d', $volume); $channel = $channel." ".$volume; $volume = @$a[3].$dezibel; } elsif ($volume % 1 == 0.5) { $volume = 40.5 if($volume < 40.5); $volume = 59.5 if($volume > 59.5); $volume = sprintf ('%03d', ($volume * 10)); $channel = $channel." ".$volume; $volume = @$a[3].$dezibel; } else { return undef; } } $return = DENON_AVR_ZONE_Command_Write($hash, "CV".$channel, "channelVolume", $volume); } elsif (@$a[1] eq "favorite") { my $favorite = @$a[2]; $return = DENON_AVR_ZONE_Command_Write($hash, "FAVORITE".$favorite, "favorite", @$a[2]); } elsif (@$a[1] eq "highPassFilter") { my $favorite = @$a[2]; my $channel = DENON_ZONE_GetValue('HPF', @$a[2]); $return = DENON_AVR_ZONE_Command_Write($hash, "HPF".$channel, "highPassFilter", @$a[2]); } elsif (@$a[1] eq "input") { my $input = DENON_ZONE_GetValue('SI', @$a[2]); $return = DENON_AVR_ZONE_Command_SetInput($hash, $input, @$a[2]); } elsif (@$a[1] eq "mute" || @$a[1] eq "muteT") { my $mute = @$a[2]; if ($mute eq "toggle" || @$a[1] eq "muteT") { my $newMuteState = DENON_ZONE_GetValue('SWITCH', ReadingsVal( $name, "mute", "off")); $return = DENON_AVR_ZONE_Command_SetMute($hash, $newMuteState); } else { $return = DENON_AVR_ZONE_Command_SetMute($hash, $mute); } } elsif (@$a[1] eq "quickselect") { my $msg = "QUICK".@$a[2]; $return = DENON_AVR_ZONE_Command_Write($hash, $msg, "quickselect", @$a[2]); } elsif (@$a[1] eq "smartselect") { my $msg = "SMART".@$a[2]; $return = DENON_AVR_ZONE_Command_Write($hash, $msg, "smartselect", @$a[2]); } elsif (@$a[1] eq "sleep") { my $msg = DENON_ZONE_GetValue('SLP', @$a[2]); $return = DENON_AVR_ZONE_Command_Write($hash, "SLP".$msg, "sleep", @$a[2]); } elsif (@$a[1] eq "autoStandby") { my $msg = DENON_ZONE_GetValue('STBY',@$a[2]); $return = DENON_AVR_ZONE_Command_Write($hash, "STBY".$msg, "autoStandby", @$a[2]); } elsif (@$a[1] eq "toggle") { my $newPowerState = DENON_ZONE_GetValue('SWITCH', ReadingsVal( $name, "state", "on")); $return =DENON_AVR_ZONE_Command_SetPower($hash, $newPowerState); } elsif (@$a[1] eq "volumeStraight") { my $volume = @$a[2]; $return = DENON_AVR_ZONE_Command_SetVolume($hash, $volume + 80); } elsif (@$a[1] eq "volume") { my $volume = @$a[2]; $return = DENON_AVR_ZONE_Command_SetVolume($hash, $volume); } elsif (@$a[1] eq "volumeDown") { my $msg = "DOWN"; my $oldVolume = ReadingsVal( $name, "volume", "0" ); my $volume = @$a[2]; if(@$a[2]) { $volume = $oldVolume - $volume; $return = DENON_AVR_ZONE_Command_SetVolume($hash, $volume); } else { readingsBulkUpdate($hash, "volumeStraight", $oldVolume - 81); readingsBulkUpdate($hash, "volume", $oldVolume - 1); $return = DENON_AVR_ZONE_Command_Write($hash, $msg, "volumeDown"); } } elsif (@$a[1] eq "volumeUp") { my $msg = "UP"; my $oldVolume = ReadingsVal( $name, "volume", "0" ); my $volume = @$a[2]; if(@$a[2]) { $volume = $oldVolume + $volume; $return = DENON_AVR_ZONE_Command_SetVolume($hash, $volume); } else { readingsBulkUpdate($hash, "volumeStraight", $oldVolume - 79); readingsBulkUpdate($hash, "volume", $oldVolume + 1); $return = DENON_AVR_ZONE_Command_Write($hash, $msg, "volumeUp"); } } elsif (@$a[1] eq "rawCommand") { my $msg = @$a[2]; $msg = @$a[2]." ".@$a[3] if defined @$a[3]; $msg = $msg." ".@$a[4] if defined @$a[4]; $return = DENON_AVR_ZONE_Command_Write($hash, $msg, "rawCommand"); } elsif (@$a[1] eq "remoteControl") { if(@$a[2] =~ /^in_(.+)/) #inputs { my $input = DENON_ZONE_GetValue('SI', $1); $return = DENON_AVR_ZONE_Command_SetInput($hash, $input, @$a[2]); } if(@$a[2] =~ /^(up|down)$/) #volume { $return = DENON_AVR_ZONE_Command_Write($hash, uc($1), "volume"); } else { fhem("set $name @$a[2]"); } } elsif (@$a[1] eq "usedInputs") { DENON_AVR_ZONE_SetUsedInputs($hash, @$a[2]); $return = ""; } else { $return = $usage; } readingsEndUpdate( $hash, 1 ); # return result return $return; } ##################################### sub DENON_AVR_ZONE_Attr($@) { my @a = @_; my $hash= $defs{$a[1]}; return undef; } ##################################### sub DENON_AVR_ZONE_Command_Write($$$;$) { my ($hash, $msg, $call, $state) = @_; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; Log3 $name, 5, "DENON_AVR_ZONE $name: zone $zone called Write"; IOWrite($hash, "Z".$zone."".$msg, "Z".$zone." ".$call); if(defined($state)) { if($state =~ /^query_(.+)/) { IOWrite($hash, "Z".$zone."".$1, "Z".$zone." query"); } else { readingsBulkUpdate($hash, $call, $state); } } return undef; } ##################################### sub DENON_AVR_ZONE_GetStateAV($) { my ($hash) = @_; my $name = $hash->{NAME}; if ( ReadingsVal( $name, "presence", "absent" ) eq "absent" ) { return "absent"; } elsif ( ReadingsVal( $name, "power", "off" ) eq "off" ) { return "off"; } elsif ( ReadingsVal( $name, "mute", "off" ) eq "on" ) { return "muted"; } else { return ReadingsVal( $name, "power", "off" ); } } ##################################### sub DENON_AVR_ZONE_Command_SetInput($$$) { my ($hash, $input, $friendlyName) = @_; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; Log3 $name, 5, "DENON_AVR_ZONE $name: zone $zone called SetInput."; IOWrite($hash, "Z".$zone."".$input, "Z".$zone." input"); readingsBulkUpdate($hash, "input", $friendlyName); return undef; } ##################################### sub DENON_AVR_ZONE_Command_SetMute($$) { my ($hash, $mute) = @_; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; Log3 $name, 5, "DENON_AVR_ZONE $name: zone $zone called SetMute."; return "mute can only used when device is powered on" if (ReadingsVal( $name, "state", "off") eq "off"); my $status = DENON_ZONE_GetValue('MU', lc($mute)); IOWrite($hash, "Z".$zone."MU".$status, "Z".$zone." mute"); readingsBulkUpdate($hash, "mute", $mute); readingsBulkUpdate($hash, "stateAV", DENON_AVR_ZONE_GetStateAV($hash)); return undef; } ##################################### sub DENON_AVR_ZONE_Command_SetPower($$) { my ($hash, $power) = @_; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; Log3 $name, 5, "DENON_AVR_ZONE $name: zone $zone called SetPower"; IOWrite($hash, "Z".$zone."".uc($power), "Z".$zone." power"); readingsBulkUpdate($hash, "power", lc($power)); readingsBulkUpdate($hash, "state", lc($power)); readingsBulkUpdate($hash, "stateAV", DENON_AVR_ZONE_GetStateAV($hash)); return undef; } ##################################### sub DENON_AVR_ZONE_Command_SetVolume($$) { my ($hash, $volume) = @_; my $name = $hash->{NAME}; my $zone = $hash->{ZONE}; my $dezibel = AttrVal($name, "unit", "off") eq "on" ? " dB" : ""; my $percent = AttrVal($name, "unit", "off") eq "on" ? " %" : ""; Log3 $name, 5, "DENON_AVR_ZONE $name: zone $zone called SetVolume."; if(ReadingsVal( $name, "state", "off") eq "off") { return "Volume can only set when device is powered on!"; } else { if (length($volume) == 1) { $volume = "0".$volume; } IOWrite($hash, "Z".$zone."".$volume, "Z".$zone." volume"); readingsBulkUpdate($hash, "volumeStraight", ($volume - 80).$percent); readingsBulkUpdate($hash, "volume", $volume.$dezibel); } return undef; } ##################################### sub DENON_AVR_ZONE_SetUsedInputs($$) { my ($hash, $usedInputs) = @_; my $name = $hash->{NAME}; my @inputs = split(/,/,$usedInputs); my @denonInputs = (); foreach (@inputs) { if(exists $DENON_db_zone->{'SI'}{$_}) { push(@denonInputs, $_); } } $attr{$name}{inputs} = join(",", @denonInputs); } ##################################### sub DENON_AVR_ZONE_RCmakenotify($$) { my ( $name, $ndev ) = @_; my $nname = "notify_$name"; fhem( "define $nname notify $name set $ndev remoteControl " . '$EVENT', 1 ); Log3 $name, 3, "DENON_AVR_ZONE $name: create notify for remoteControl."; return "Notify created by DENON_AVR_ZONE $nname"; } ##################################### sub DENON_AVR_ZONE_RCmake($) { my ( $name ) = @_; my $device = $name."_RC"; if(!defined($defs{$device})) { fhem("define $device remotecontrol"); fhem("sleep 1;set $device layout DENON_AVR_ZONE_RC"); my $notify = "notify_$name"; if(!defined($defs{$notify})) { fhem("sleep 1;set $device makenotify $name"); } } Log3 $name, 3, "DENON_AVR $name: create remoteControl."; return "Remotecontrol created by DENON_AVR $name"; } ##################################### sub DENON_AVR_ZONE_RCdelete($) { my ( $name ) = @_; my $device = $name."_RC"; if(defined($defs{$device})) { fhem("delete $device"); my $notify = "notify_".$device; if(defined($defs{$notify})) { fhem("sleep 1;delete $notify"); } } Log3 undef, 3, "DENON_AVR $name: delete remoteControl."; return "Remotecontrol deleted by DENON_AVR_ZONE: $name"; } ##################################### sub DENON_AVR_ZONE_RClayout() { my @row; $row[0] = "volumeUp:VOLUP,:blank,in_Cbl/Sat:CBLSAT,in_Blu-Ray:BR,in_DVD:DVD,in_CD:CD,:blank,play:PLAY,:blank,toggle:POWEROFF3"; $row[1] = "muteT:MUTE,:blank,in_Mediaplayer:MEDIAPLAYER,in_iRadio:IRADIO,in_OnlineMusic:ONLINEMUSIC,in_Usb/iPod:IPODUSB,:blank,pause:PAUSE"; $row[2] = "volumeDown:VOLDOWN,:blank,in_Bluetooth:BT,in_Favorites:FAV,in_Aux1:AUX1,in_Phono:PHONO,:blank,stop:STOP"; $row[3] = "attr rc_iconpath icons/remotecontrol"; $row[4] = "attr rc_iconprefix black_btn_"; return @row; } 1; =pod =item device =item summary control for DENON (Marantz) AV receivers zone =item summary_DE Zonen-Steuerung von DENON (Marantz) AV Receivern =begin html

DENON_AVR_ZONE

    Define
      define <name> DENON_AVR_ZONE <zonenumber>

      This module controls DENON A/V receivers zones.


      Example:

        define avr_zone2 DENON_AVR_ZONE 2

        define avr_zone2 DENON_AVR_ZONE 3



    Set
      set <name> <command> [<parameter>]

      Currently, the following commands are defined:
      • autoStandby   -   set auto standby time
      • favorite   -   switches between favorite (only older models)
      • input   -   switches between inputs
      • mute on,off   -   controls volume mute
      • muteT   -   toggle mute state
      • off   -   turns the device in standby mode
      • on   -   powers on the device
      • quickselect   -   switches between quick select modes (1-5, only new models)
      • rawCommand   -   send raw command to AV receiver
      • remote   -   remote commands (play, stop, pause,...)
      • surroundMode   -   set surround mode
      • toggle   -   switch between on and off
      • volume 0...98   -   set the volume level in percentage
      • volumeStraight -80...18   -   set the volume level in dB
      • volumeUp   -   increases the volume level
      • volumeDown   -   decreases the volume level

    Get
      get <name> <what>

      Currently, the following commands are defined:
      • remoteControl   -   autocreate remote ccontrol
      • some readings   -   see list below


    Generated Readings/Events:

    • autoStandby   -   auto standby state
    • input   -   selected input
    • mute   -   mute state
    • power   -   power state
    • state   -   state of AV reciever (on,off,disconnected)
    • stateAV   -   state of AV reciever (on,off,absent,mute)
    • treble   -   treble level in dB
    • videoSelect   -   actual video select mode
    • volume   -   actual volume
    • volumeMax   -   actual maximum volume
    • volumeStraight   -   actual volume straight

    Attributes

    • IODev   -   Input/Output Device
=end html =begin html_DE

DENON_AVR_ZONE

    Define
      define <name> DENON_AVR_ZONE <zonename[:PORT]>

      Dieses Modul steuert DENON A/V Receiver über das Netzwerk.


      Beispiele:

        define avr_zone2 DENON_AVR_ZONE 2

        define avr_zone3 DENON_AVR_ZONE 3



    Set
      set <name> <command> [<parameter>]

      Momentan sind folgende Befehle verfügbar:
      • autoStandby   -   Zeit für den Auto-Standby setzen
      • favorite   -   zur Auswahl der Favoriten (nur alte Modelle)
      • input   -   zur Auswahl der Eingänge
      • mute an,aus   -   AV-Receiver laut/stumm schalten
      • muteT   -   zwischen laut und stumm wechseln
      • off   -   Standby AV-Receiver
      • on   -   AV-Receiver anschalten
      • quickselect   -   zur Auswahl der "Quick-Select" Modi (1-5, nur neue Modelle)
      • rawCommand   -   schickt ein "raw command" zum AV-Receiver
      • remote   -   Fernbedienungsbefehle (play, stop, pause,...)
      • surroundMode   -   zur Auswahl der Surround-Modi
      • toggle   -   AV-Receiver an/aus
      • volume 0...98   -   Lautstärke in Prozent
      • volumeStraight -80...18   -   absolute Lautstärke in dB
      • volumeUp   -   erhöht Lautstärke
      • volumeDown   -   erniedrigt Lautstärke


    Get
      get <name> <what>

      Momentan sind folgende Befehle verfügbar:

      • remoteControl   -   Fernbedienung automatisch erzeugen
      • diverse Readings   -   siehe Liste unten


    Erzeugte Readings/Events:

    • autoStandby   -   Standbyzustand des AV-Recievers
    • bass   -   Bass-Level in dB
    • display   -   Dim-Status des Displays
    • input   -   gewählte Eingangsquelle
    • levelFrontLeft   -   Pegel des linken Frontlautsprechers in dB
    • levelFrontRight   -   Pegel des rechten Frontlautsprechers in dB
    • mute   -   Status der Stummschaltung
    • power   -   Einschaltzustand des AV-Recievers
    • sound   -   aktueller Sound-Modus
    • state   -   Status des AV-Recievers (on,off,disconnected)
    • stateAV   -   stateAV-Status des AV-Recievers (on,off,mute,absent)
    • toneControl   -   Status der Klangkontrolle
    • treble   -   Höhen-Level in dB
    • videoSelect   -   gewählter Videoselect-Modus
    • volume   -   aktuelle Lautstärke in Prozent
    • volumeMax   -   maximale Lautstärke in Prozent
    • volumeStraight   -   aktuelle absolute Lautstärke in dB

    Attribute

    • IODev   -   Input/Output Device
=end html_DE =cut