diff --git a/fhem/CHANGED b/fhem/CHANGED index 3ab8c12fd..b724e0e45 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 71_PHILIPS_AUDIO: Module rewritten for better usability. + New commands. See documentation. - update: 38_CO20: identify devices by id / serial number - update: 38_netatmo: dynamic update intervals for devices - update: 98_weekprofile: update reading profile_count after save profiles diff --git a/fhem/FHEM/71_PHILIPS_AUDIO.pm b/fhem/FHEM/71_PHILIPS_AUDIO.pm index baa29a1e4..9553d5318 100755 --- a/fhem/FHEM/71_PHILIPS_AUDIO.pm +++ b/fhem/FHEM/71_PHILIPS_AUDIO.pm @@ -1,32 +1,31 @@ +# $Id$ ############################################################################## # -# $Id$ +# 71_PHILIPS_AUDIO.pm # -# 71_PHILIPS_AUDIO.pm +# An FHEM Perl module for controlling Philips Audio Equipment connected to +# local network such as MCi, Streamium and Fidelio devices. +# The module provides basic functionality accessible through the port 8889 +# of the device: (http://:8889/index) +# e.g. AW9000, NP3500, NP3700, NP3900 # -# An FHEM Perl module for controlling Philips Audio Equipment connected to local network -# such as MCi, Streamium and Fidelio devices. -# The module provides basic functionality accessible through the port 8889 of the device: -# (http://:8889/index). -# e.g. AW9000, NP3500, NP3700, NP3900 +# Copyright by ra666ack +# (e-mail: ra666ack at g**glemail d*t c*m) # -# Copyright by Radoslaw Watroba -# (e-mail: ra666ack at g**glemail d*t c*m) +# This file is part of fhem. # -# 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 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. # -# 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 . +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . # ############################################################################## @@ -39,7 +38,6 @@ use Time::Piece; use POSIX qw{strftime}; use HttpUtils; -################################### sub PHILIPS_AUDIO_Initialize { my ($hash) = @_; @@ -50,12 +48,19 @@ sub PHILIPS_AUDIO_Initialize $hash->{AttrFn} = "PHILIPS_AUDIO_Attr"; $hash->{UndefFn} = "PHILIPS_AUDIO_Undefine"; - $hash->{AttrList} = "do_not_notify:0,1 disable:0,1 model max-device-presets max-device-favorites request-timeout:1,2,3,4,5 ".$readingFnAttributes; - + $hash->{AttrList} = "do_not_notify:0,1 ". + "disable:0,1 ". + "autoGetPresets:0,1 ". + "autoGetFavorites:0,1 ". + "httpBufferTimeout ". + "maxListItems ". + "model ". + "playerBrowsingTimeout ". + "requestTimeout ". + "$readingFnAttributes"; return; } -################################### sub PHILIPS_AUDIO_GetStatus { my ($hash, $local) = @_; @@ -64,44 +69,115 @@ sub PHILIPS_AUDIO_GetStatus $local = 0 unless(defined($local)); - return "" if((!defined($hash->{IP_ADDRESS})) or (!defined($hash->{helper}{OFF_INTERVAL})) or (!defined($hash->{helper}{ON_INTERVAL}))); - + return if((!defined($hash->{IP_ADDRESS})) or (!defined($hash->{helper}{OFF_INTERVAL})) or (!defined($hash->{helper}{ON_INTERVAL}))); my $device = $hash->{IP_ADDRESS}; + # First run + $hash->{helper}{networkRequest} = "idle" if (not defined($hash->{helper}{networkRequest})); + PHILIPS_AUDIO_getMediaRendererDesc($hash) if(not defined($hash->{helper}{dInfo}{UUID})); + #$hash->{helper}{manualOperation} = 0 if(not defined($hash->{helper}{manualOperation})); + + if (not defined($hash->{helper}{playerState})) + { + # First run. Go to index. + $hash->{helper}{playerState} = "home"; + readingsSingleUpdate($hash, "playerState", "home", 1); + PHILIPS_AUDIO_SendCommand($hash, "/index", "","home", "noArg"); + PHILIPS_AUDIO_ResetTimer($hash); # getStatus + return; + } + + if($hash->{helper}{playerState} eq "home") + { + # Check if device playing. Might by activated by the remote control or app. + # If not, the device returns 'NOTHING'; + PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); + + # Heartbeat + #PHILIPS_AUDIO_SendCommand($hash, "/HOMESTATUS", "","homestatus", "noArg"); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "input", "-"); + readingsBulkUpdate($hash, "playerState", "home"); + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsEndUpdate($hash, 1); + } + elsif($hash->{helper}{playerState} eq "browsing") + { + # Do nothing and check for inactivity duration + + $hash->{helper}{playerBrowsingTimeout} = 0 if(not defined ($hash->{helper}{playerBrowsingTimeout})); + + $hash->{helper}{playerBrowsingTimeout} += $hash->{helper}{ON_INTERVAL}; + + # reset browsing state after 3 minutes inactivity in order to update the readings automatically again + if($hash->{helper}{playerBrowsingTimeout} >= int(AttrVal($name, "playerBrowsingTimeout", 180))) + { + $hash->{helper}{playerState} = "home"; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "input", "-"); + readingsBulkUpdate($hash, "playerState", "home"); + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsEndUpdate($hash, 1); + $hash->{helper}{playerBrowsingTimeout} = 0; + } + } + elsif($hash->{helper}{playerState} eq "playing") + { + PHILIPS_AUDIO_SendCommand($hash, "/ELAPSE", "","elapse", "noArg"); + } # Check for Presets availability - if(not defined($hash->{READINGS}{"totalInetRadioPresets"})) + if + ( + (not defined($hash->{READINGS}{"totalPresets"})) and + (ReadingsVal($name, "presence", "no") eq "present") and + (AttrVal($name, "autoGetPresets", "0") eq "1") + ) { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getInetRadioPresets", "noArg"); - # Wait 10 seconds for next http request due to slow command processing of the streamium devices. - PHILIPS_AUDIO_ResetTimer($hash, 10); + readingsSingleUpdate($hash, "Reading_presets" , "May take some time...", 1); + # Hierarchichal navigation through the contents mandatory + $hash->{helper}{cmdStep} = 1; + PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getPresets", "noArg") if((ReadingsVal($name, "playerListStatus", "ready") eq "ready") and (ReadingsVal($name, "readingPresets", "no") eq "no")); + PHILIPS_AUDIO_ResetTimer($hash, 10); # Scan takes approx 8 sec. return; } - - # Check for Favorites availability - if(not defined($hash->{READINGS}{"totalInetRadioFavorites"})) - { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getInetRadioFavorites", "noArg"); - # Wait 10 seconds for next http request due to slow command processing of the streamium devices. - PHILIPS_AUDIO_ResetTimer($hash, 10); - return; - } - - PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); - PHILIPS_AUDIO_ResetTimer($hash) unless($local == 1); + # Check for Favorites availability + if + ( + (not defined($hash->{READINGS}{"totalFavorites"})) and + (ReadingsVal($name, "presence", "no") eq "present") and + (AttrVal($name, "autoGetFavorites", "0") eq "1") + ) + { + readingsSingleUpdate($hash, "Reading_favorites" , "May take some time...", 1); + # Hierarchichal navigation through the contents mandatory + $hash->{helper}{cmdStep} = 1; + PHILIPS_AUDIO_SendCommand($hash, "/index", "", "getFavorites", "noArg") if((ReadingsVal($name, "playerListStatus", "ready") eq "ready") and (ReadingsVal($name, "readingFavorites", "no") eq "no")); + PHILIPS_AUDIO_ResetTimer($hash, 10); # Scan takes approx 8 sec. + return; + } + + # Don't interfere with manual operation. Will cause wrong responses from device. + #if + #( + # (ReadingsVal($name, "playerListStatus", "ready") eq "ready") and + # ($hash->{helper}{manualOperation} == 0) and + # ($hash->{helper}{playerState} eq "home") + #) + #{ + # #PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); + #} + + PHILIPS_AUDIO_ResetTimer($hash) if(not ($local == 1)); # getStatus return; } -################################### sub PHILIPS_AUDIO_Get { my ($hash, @a) = @_; - my $what; + my $what = $a[1]; my $return; my $name = $hash->{NAME}; @@ -109,45 +185,43 @@ sub PHILIPS_AUDIO_Get my $address = $hash->{IP_ADDRESS}; $hash->{IP_ADDRESS} = $address; - return "argument is missing" if(int(@a) != 2); + return "Argument missing." if(int(@a) < 2); - if(not defined($hash->{MODEL})) + if($what eq "reading") { - return "Please provide the model information as argument."; - } - - $what = $a[1]; - - if(exists($hash->{READINGS}{$what})) + if(exists($hash->{READINGS}{$a[2]})) + { + if(defined($hash->{READINGS}{$a[2]})) + { + return $hash->{READINGS}{$a[2]}{VAL}; + } + else + { + return "No such reading: $what"; + } + } + } + elsif($what eq "deviceInfo") { - if(defined($hash->{READINGS}{$what})) - { - return $hash->{READINGS}{$what}{VAL}; - } - else - { - return "no such reading: $what"; - } + return "Device info:\n\n". + join("\n", map {sprintf("%-17s: %-17s", $_, $hash->{helper}{dInfo}{$_})} sort keys %{$hash->{helper}{dInfo}}); } else { - $return = "unknown argument $what, choose one of"; - - foreach my $reading (keys %{$hash->{READINGS}}) - { - $return .= " $reading:noArg"; - } - + $return = "Unknown argument $what, choose one of" + ." deviceInfo:noArg" + ." reading:".(join(",",(sort keys %{$hash->{READINGS}}))); + return $return; } } -################################### sub PHILIPS_AUDIO_Set { my ($hash, @a) = @_; my $name = $hash->{NAME}; my $port = $hash->{PORT}; + my $address = $hash->{IP_ADDRESS}; if(not defined($hash->{MODEL})) { @@ -157,104 +231,203 @@ sub PHILIPS_AUDIO_Set return "No Argument given" if(!defined($a[1])); my $what = $a[1]; - my $index; - - # Provide number of selectable presets according to the given attribute - my $inetRadioPreset = "inetRadioPreset:"; - for ($index = 1; $index < AttrVal($name, "max-device-presets", "24"); $index++) - { - $inetRadioPreset .= $index . ","; - } - $inetRadioPreset .= $index . " "; - - # Provide number of selectable favorites according to the given attribute - my $inetRadioFavorite = "inetRadioFavorite:"; - for ($index = 1; $index < AttrVal($name, "max-device-favorites", "24"); $index++) - { - $inetRadioFavorite .= $index . ","; - } - $inetRadioFavorite .= $index . " "; - - my $usage; + my $usage = ""; my $model = $hash->{MODEL}; + $hash->{helper}{dInfo}{MODEL} = $model; + $hash->{helper}{dInfo}{NAME} = $name; + $hash->{helper}{dInfo}{PORT} = $port; + $hash->{helper}{dInfo}{IP_ADDRESS} = $address; - $usage = "Unknown argument $what, choose one of ". - "volumeStraight:slider,0,1,64 ". - "volume:slider,0,1,100 ". - #"volumeUp:noArg ". - #"volumeDown:noArg ". - "standbyButton:noArg ". - "unmute:noArg ". - "next:noArg ". - "previous:noArg ". - "play_pause:noArg ". - "stop:noArg ". - "shuffle:on,off ". - "aux:noArg ". - ((uc($model) eq "AW9000") ? "mp3link:noArg " : ""). # Input implemented in AW9000 only - ((uc($model) eq "AW9000") ? "coaxial:noArg " : ""). # Input implemented in AW9000 only - ((uc($model) eq "AW9000") ? "optical:noArg " : ""). # Input implemented in AW9000 only - #"input:aux,internetRadio,mediaLibrary,onlineServices ". - $inetRadioPreset. - $inetRadioFavorite. - "statusRequest:noArg ". - "getInetRadioPresets:noArg ". - "getInetRadioFavorites:noArg ". - ((exists($hash->{helper}{PRESETS}))? "inetRadioPresetByName:".($hash->{helper}{PRESETS})." ":""). - ((exists($hash->{helper}{FAVORITES}))? "inetRadioFavoriteByName:".($hash->{helper}{FAVORITES})." ":""). - #"addToFavourites:noArg ". - #"removeFromFavourites:noArg ". - "repeat:single,all,off ". - #"home:noArg ". - "mute:noArg "; + $usage = "Unknown argument $what, choose one of". + " volumeStraight:slider,0,1,64 ". + " volume:slider,0,1,100 ". + " volumeUp:noArg ". + " volumeDown:noArg ". + " standbyButton:noArg ". + " player:next,prev,play-pause,stop ". + " shuffle:on,off ". + " input:---,". + + ((uc($model) eq "AW9000") ? "analogAux," : ""). # Input implemented in AW9000 only + ((uc($model) eq "AW9000") ? "digital1Coaxial," : ""). # Input implemented in AW9000 only + ((uc($model) eq "AW9000") ? "digital2Optical," : ""). # Input implemented in AW9000 only + + # Input not implelemnted in the AW9000. Only as DLNA renderer + ((uc($model) ne "AW9000") ? "mediaLibrary," : ""). + + "internetRadio,onlineServices,mp3Link ". # Available in all devices + #" statusRequest:noArg". + " getPresets:noArg". + " getFavorites:noArg". + " getMediaRendererDesc:noArg". + " favoriteAdd:noArg". + " favoriteRemove:noArg". + " repeat:single,all,off". + #" home:noArg". + " mute:on,off "; + + my @favoriteList; + my @favoriteNumber; + foreach my $readings (keys % {$hash->{READINGS}}) + { + push @favoriteList,$1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^.inetRadioFavorite_(.*)/); + push @favoriteNumber, $1 if($readings =~ m/^.inetRadioFavorite_(.*)/); + } + + (s/\*/\[asterisk\]/g) for @favoriteList; # '*' not shown correctly + (s/#/\[hash\]/g) for @favoriteList; # '#' not shown correctly + (s/[ :;,']/_/g) for @favoriteList; # Replace not allowed characters + + my @presetList; + my @presetNumber; + foreach my $readings (keys % {$hash->{READINGS}}) + { + push @presetList, $1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^.inetRadioPreset_(.*)/); + push @presetNumber, $1 if($readings =~ m/^.inetRadioPreset_(.*)/); + } + + (s/\*/\[asterisk\]/g) for @presetList; # '*' not shown correctly + (s/#/\[hash\]/g) for @presetList; # '#' not shown correctly + (s/[ :;,']/_/g) for @presetList; # Replace not allowed characters + + $usage .= "selectFavorite:" .join(",",("---",(sort @favoriteList))) . " "; + $usage .= "selectPreset:" .join(",",("---",(sort @presetList))) . " "; + $usage .= "selectPresetByNumber:" .join(",",("---",(sort @presetNumber))) . " "; + $usage .= "selectFavoriteByNumber:".join(",",("---",(sort @favoriteNumber))) . " "; + + # Direct stream selection if any + + my @selectStream; + + for(my $lvl = 1; $lvl < int(ReadingsVal($name, ".listDepthLevel", "1") - 1); $lvl++) + { + my $listLevelName = $hash->{READINGS}{".lvl_".$lvl."_name"}; + push @selectStream, "lvl_".$lvl."_".$listLevelName; + } + + foreach my $readings (keys % {$hash->{READINGS}}) + { + push @selectStream,$1."_".substr($hash->{READINGS}{$readings}{VAL}, 0, 25) if($readings =~ m/^listItem_(.*)/); + } + + @selectStream = sort map{s/\*/\[asterisk\]/g;$_;} grep/._..*$/,@selectStream; # Replace * + @selectStream = sort map{s/#/\[hash\]/g;$_;} grep/._..*$/,@selectStream; # Replace # + @selectStream = sort map{s/[ :;,']/_/g;$_;} grep/._..*$/,@selectStream; # Replace not allowed characters + + $usage .= "selectStream:".join(",",("---",(sort @selectStream))) . " "; Log3 $name, 5, "PHILIPS_AUDIO ($name) - set ".join(" ", @a); - + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + + if($what =~ /input|selectStream|selectPreset|selectFavorite/) + { + # GetStatus while manual operation causes the device to stuck. Supress. + # Change device state to "browsing" in order to suppress automatic update + $hash->{helper}{playerState} = "browsing"; + $hash->{helper}{manualOperation} = 1; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "playerState", "browsing"); + readingsBulkUpdate($hash, ".manualOperation", "yes", 1); + readingsEndUpdate($hash, 1); + # Reset browsing timeout + $hash->{helper}{playerBrowsingTimeout} = 0; + } + if($what eq "standbyButton") { + readingsSingleUpdate($hash, "input", "-", 1); PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STANDBY", "",$what, "noArg"); } - elsif($what eq "aux") + elsif($what eq "getMediaRendererDesc") { - PHILIPS_AUDIO_SendCommand($hash, "/aux", "",$what, $a[2]); - } - elsif($what eq "mp3link") + PHILIPS_AUDIO_getMediaRendererDesc($hash); + } + elsif($what eq "input") { - PHILIPS_AUDIO_SendCommand($hash, "/mp3link", "",$what, $a[2]); - } - elsif($what eq "coaxial") - { - PHILIPS_AUDIO_SendCommand($hash, "/digin_coaxial", "",$what, $a[2]); - } - elsif($what eq "optical") - { - PHILIPS_AUDIO_SendCommand($hash, "/digin_optical", "",$what, $a[2]); - } + if ($a[2] =~ /analogAux|mp3Link|digital1Coaxial|digital2Optical/) + { + # Delete List related readings + delete $hash->{READINGS}{$_} foreach (grep /list/, keys %{$hash->{READINGS}}); + # Delete player related readings + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + } + + if($a[2] eq "analogAux") + { + readingsSingleUpdate($hash, "input", "Aux-in (analog)", 1); + PHILIPS_AUDIO_SendCommand($hash, "/aux", "",$what, $a[2]); + } + elsif($a[2] eq "mp3Link") + { + readingsSingleUpdate($hash, "input", "MP3-Link", 1); + $hash->{helper}{playerState} = "home"; + readingsSingleUpdate($hash, "playerState", "home", 1); + if(uc($model) eq "AW9000") + { + PHILIPS_AUDIO_SendCommand($hash, "/mp3link", "", $what, $a[2]); + } + else + { + PHILIPS_AUDIO_SendCommand($hash, "/aux", "", $what, $a[2]); + } + } + elsif($a[2] eq "digital1Coaxial") + { + readingsSingleUpdate($hash, "input", "Digital-in 1 (coaxial)", 1); + $hash->{helper}{playerState} = "home"; + readingsSingleUpdate($hash, "playerState", "home", 1); + PHILIPS_AUDIO_SendCommand($hash, "/digin_coaxial", "",$what, $a[2]); + + } + elsif($a[2] eq "digital2Optical") + { + readingsSingleUpdate($hash, "input", "Digital-in 2 (optical)", 1); + $hash->{helper}{playerState} = "home"; + readingsSingleUpdate($hash, "playerState", "home", 1); + PHILIPS_AUDIO_SendCommand($hash, "/digin_optical", "",$what, $a[2]); + } + elsif($a[2] eq "mediaLibrary") + { + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + #readingsSingleUpdate($hash, "input", "Media Library", 1); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$02\$01\$001\$0", "", $what, $a[2]); + } + elsif($a[2] eq "internetRadio") + { + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + #readingsSingleUpdate($hash, "input", "Internet Radio", 1); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "",$what, $a[2]); + } + elsif($a[2] eq "onlineServices") + { + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + #readingsSingleUpdate($hash, "input", "Online Services", 1); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$09\$01\$001\$0", "", $what, $a[2]); + } + else + { + return $usage; + } + } elsif($what eq "home") { - PHILIPS_AUDIO_SendCommand($hash, "/index", "",$what, $a[2]); - } - elsif($what eq "mediaLibrary") - { - PHILIPS_AUDIO_SendCommand($hash, "/nav\$02\$01\$001\$0", "",$what, $a[2]); - } - elsif($what eq "internetRadio") - { - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "",$what, $a[2]); - } - elsif($what eq "onlineServices") - { - PHILIPS_AUDIO_SendCommand($hash, "/nav\$09\$01\$001\$0", "",$what, $a[2]); + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + $hash->{header}{httpHeaderRefer} = "Upgrade-Insecure-Requests: 1\r\n"; + PHILIPS_AUDIO_SendCommand($hash, "/index", "",$what, "noArg"); } elsif($what eq "shuffle") { if($a[2] eq "on") { + readingsSingleUpdate($hash, "playerShuffle", "on", 1); PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_ON", "",$what, $a[2]); } elsif($a[2] eq "off") { + readingsSingleUpdate($hash, "playerShuffle", "off", 1); PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_OFF", "",$what, $a[2]); } else @@ -266,14 +439,17 @@ sub PHILIPS_AUDIO_Set { if($a[2] eq "single") { + readingsSingleUpdate($hash, "playerRepeat", "single", 1); PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_SINGLE", "",$what, $a[2]); } elsif($a[2] eq "all") { + readingsSingleUpdate($hash, "playerRepeat", "all", 1); PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_ALL", "",$what, $a[2]); } elsif($a[2] eq "off") { + readingsSingleUpdate($hash, "playerRepeat", "off", 1); PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_OFF", "",$what, $a[2]); } else @@ -285,128 +461,278 @@ sub PHILIPS_AUDIO_Set { PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); } - elsif($what eq "addToFavourites") + elsif($what eq "favoriteAdd") { PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$ADD2FAV", "",$what, "noArg"); } - elsif($what eq "removeFromFavourites") + elsif($what eq "favoriteRemove") { PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$REMFAV", "",$what, "noArg"); } elsif($what eq "mute") { - PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$MUTE", "",$what, "noArg"); - } - elsif($what eq "unmute") - { - PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$UNMUTE", "",$what, "noArg"); - } - elsif($what eq "next") - { - PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$NEXT", "",$what, "noArg"); - } - elsif($what eq "previous") - { - PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PREV", "",$what, "noArg"); - } - elsif($what eq "play_pause") - { - PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PLAY_PAUSE", "",$what, "noArg"); - } - elsif($what eq "stop") - { - PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STOP", "",$what, "noArg"); - } - elsif($what eq "inetRadioPreset") - { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - $hash->{helper}{inetRadioPreset} = $a[2]; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); - } - elsif($what eq "inetRadioPresetByName") - { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - my ($presetNumber, $presetName) = split(/:/, $a[2], 2); - $hash->{helper}{inetRadioPresetNumber} = $presetNumber; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); - } - elsif($what eq "inetRadioFavorite") - { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - $hash->{helper}{inetRadioFavorite} = $a[2]; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); - } - elsif($what eq "inetRadioFavoriteByName") - { - # Hierarchichal navigation through the contents mandatory - $hash->{helper}{cmdStep} = 1; - my ($favoriteNumber, $favoriteName) = split(/:/, $a[2], 2); - $hash->{helper}{inetRadioFavoriteNumber} = $favoriteNumber; - PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); - } - elsif($what eq "volumeStraight") - { - if($a[2] >= 0 and $a[2] <= 64) + if($a[2] eq "on") { - $hash->{helper}{targetVolume} = $a[2]; - PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]); + readingsSingleUpdate($hash, "mute", "on", 1); + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$MUTE", "",$what, "noArg"); + } + elsif($a[2] eq "off") + { + readingsSingleUpdate($hash, "mute", "off", 1); + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$UNMUTE", "",$what, "noArg"); } else { - return "volumeStraight must be in the range 0...64."; + return $usage; + } + } + elsif($what eq "player") + { + if($a[2] eq "next") + { + PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$NEXT", "",$what, "noArg"); } + elsif($a[2] eq "prev") + { + PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PREV", "",$what, "noArg"); + } + elsif($a[2] eq "play-pause") + { + PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$PLAY_PAUSE", "",$what, "noArg"); + } + elsif($a[2] eq "stop") + { + PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STOP", "",$what, "noArg"); + } + else + { + return $usage; + } + } + elsif($what =~ m/^(selectPreset|selectPresetByNumber)/) + { + if($a[2] ne "---") + { + # Hierarchichal navigation through the contents mandatory + $hash->{helper}{cmdStep} = 1; + + my $presetNumber = substr($a[2], 0, 2); # Get 2 first digits + + if($a[2] =~ m/empty/ or ReadingsVal($name, ".inetRadioPreset_$presetNumber","") eq "empty") + { + # Do nothing + } + else + { + $presetNumber =~ s/^0+//g; # Remove leading '0' + $hash->{helper}{inetRadioPreset} = $presetNumber; + readingsSingleUpdate($hash, "input", "Internet Radio", 1); + PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); + } + } + } + elsif($what =~ m/^(selectFavorite|selectFavoriteByNumber)/) + { + if($a[2] ne "---") + { + # Hierarchichal navigation through the contents mandatory + $hash->{helper}{cmdStep} = 1; + + my $favoriteNumber = substr($a[2], 0, 2); # Get 2 first digits + + if($a[2] =~ m/empty/ or ReadingsVal($name, ".inetRadioFavorite_$favoriteNumber","") eq "empty") + { + # Do nothing + } + else + { + $favoriteNumber =~ s/^0+//g; # Remove leading '0' + $hash->{helper}{inetRadioFavorite} = $favoriteNumber; + #readingsSingleUpdate($hash, "input", "Internet Radio", 1); + PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, $a[2]); + } + } + } + elsif($what eq "volumeStraight") + { + if(($a[2] < 0) || ($a[2] > 64)) + { + return "volumeStraight must be in the range 0...64."; + } + else + { + $hash->{helper}{targetVolume} = int($a[2]); + readingsSingleUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume}, 1); + readingsSingleUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}), 1); + + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]); + } + } + elsif($what eq "volumeUp") + { + my $targetVolume = int(int(ReadingsVal($name, "volumeStraight", "0")) + 1); + + if($targetVolume > 64) + { + $targetVolume = 64; + } + + $hash->{helper}{targetVolume} = $targetVolume; + + readingsSingleUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume}, 1); + readingsSingleUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}), 1); + + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$" . $hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume}); + } + elsif($what eq "volumeDown") + { + my $targetVolume = int(int(ReadingsVal($name, "volumeStraight", "0")) - 1); + + if($targetVolume < 0) + { + $targetVolume = 0; + } + + $hash->{helper}{targetVolume} = $targetVolume; + + readingsSingleUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume}, 1); + readingsSingleUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $hash->{helper}{targetVolume}), 1); + + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$" . $hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume}); } elsif($what eq "volume") { - if($a[2] >= 0 and $a[2] <= 100) + if(($a[2] < 0) || ($a[2] > 100)) { - $hash->{helper}{targetVolume} = PHILIPS_AUDIO_volume_rel2abs($hash, $a[2]); - PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]); + return "volumeStraight must be in the range 0...100."; } else { - return "volumeStraight must be in the range 0...100."; + $hash->{helper}{targetVolume} = PHILIPS_AUDIO_volume_rel2abs($hash, $a[2]); + + readingsSingleUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume}, 1); + readingsSingleUpdate($hash, "volume", $a[2], 1); + + PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$hash->{helper}{targetVolume}, "",$what, $hash->{helper}{targetVolume}); } } elsif($what eq "nowplay") { PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "",$what, "noArg"); } - elsif($what eq "getInetRadioPresets") + elsif($what eq "homestatus") { - - delete($hash->{helper}{PRESETS}); - + $hash->{helper}{httpHeaderRefer} = "http://$hash->{IP_ADDRESS}:$hash->{PORT}/index\r\n"; + PHILIPS_AUDIO_SendCommand($hash, "/HOMESTATUS", "",$what, "noArg"); + } + elsif($what eq "getPresets") + { + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + readingsSingleUpdate($hash, "readingPresets", "yes", 1); + # Delete old redings + delete $hash->{READINGS}{$_} foreach (grep /.inetRadioPreset_..$/, keys %{$hash->{READINGS}}); + # Hierarchichal navigation through the contents mandatory $hash->{helper}{cmdStep} = 1; PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, "noArg"); } - elsif($what eq "getInetRadioFavorites") + elsif($what eq "getFavorites") { - # Hierarchichal navigation through the contents mandatory + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + readingsSingleUpdate($hash, "readingFavorites", "yes", 1); + # Delete old redings + delete $hash->{READINGS}{$_} foreach (grep /.inetRadioFavorite_..$/, keys %{$hash->{READINGS}}); + + # Hierarchichal navigation through the contents mandatory $hash->{helper}{cmdStep} = 1; PHILIPS_AUDIO_SendCommand($hash, "/index", "", $what, "noArg"); } + elsif($what eq "selectStream") + { + + # The player list selection has been designed for GUI. + # Virtually scrolling down to last item and choosing the first one + # arises an error. Needs to navigate back to the corresponding page + # consisting of 8 items per page + + readingsSingleUpdate($hash, "playerListStatus", "busy", 1); + + if($a[2] =~ /lvl_(.+?)/) + { + my $desiredLevel = $1; + my $currentUrl = $hash->{helper}{currentNavUrl}; + my $newUrl = ""; + my $currentLevel = ""; + my $newLevel = ""; + + if($currentUrl =~ /\/nav\$(.*)\$(.*)\$(.*)\$(.*)/) # e.g. /nav$02$XX$001$0 + { + $currentLevel = int($2); + $newLevel = sprintf("%02d", $currentLevel - ($currentLevel - $desiredLevel) + 1); + $newUrl = "/nav\$$1\$$newLevel\$$3\$0"; + } + + PHILIPS_AUDIO_SendCommand($hash, $newUrl, "", $what, $a[2]); + } + elsif($a[2] ne "---") + { + my $targetNr = substr($a[2], 0, 3); + my $currentUrl = $hash->{helper}{currentNavUrl}; + + # Remark TODO... + + if($a[2] =~ /(\d{3})_(.+?)_(.*)/) + { + my $currentListLevel = ReadingsVal($name, ".listDepthLevel", ""); + $hash->{READINGS}{".lvl_".$currentListLevel."_name"} = "$2_$3"; + + if(int(ReadingsVal($name, "playerListTotalCount", "8")) > 8) + { + if($currentUrl =~ /\/nav\$(.*)\$(.*)\$(.*)\$(.*)/) + { + # Virtually scroll back to the first line + + my $scrollUrl = "\/nav\$$1\$$2\$001\$$4"; + + PHILIPS_AUDIO_SendCommand($hash, $scrollUrl, "", "selectStream", "scroll"); + + # int(($targetNr / 8) + 0.5) -> Round up + for(my $i = 1; $i < (int(($targetNr / 8) + 0.5) + 1); $i += 8) # Max 8 items in the actual list + { + my $scrollUrl = "\/nav\$$1\$$2\$".sprintf("%003s", ($i + 8))."\$$4"; + + PHILIPS_AUDIO_SendCommand($hash, $scrollUrl, "", "selectStream", "scroll"); + } + } + } + } + PHILIPS_AUDIO_SendCommand($hash, ReadingsVal($name, ".listItemTarget_$targetNr", ""), "", $what, $a[2]); + } + else + { + return $usage; + } + } else { return $usage; } - PHILIPS_AUDIO_ResetTimer($hash); + PHILIPS_AUDIO_ResetTimer($hash); # Reset timer for the next loop return; } -############################# sub PHILIPS_AUDIO_Define { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; + $hash->{helper}{sendCommandBuffer} = []; + + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + if(! @a >= 4) { my $msg = "Wrong syntax: define PHILIPS_AUDIO [] [] "; @@ -423,7 +749,7 @@ sub PHILIPS_AUDIO_Define } $hash->{IP_ADDRESS} = $a[3]; - $hash->{PORT} = 8889; + $hash->{PORT} = 8889; # if an update interval was given which is greater than zero, use it. if(defined($a[4]) and $a[4] > 0) @@ -462,130 +788,351 @@ sub PHILIPS_AUDIO_Define # start the status update timer $hash->{helper}{DISABLED} = 0 unless(exists($hash->{helper}{DISABLED})); - PHILIPS_AUDIO_ResetTimer($hash,0); + + + PHILIPS_AUDIO_ResetTimer($hash, 0); return; } +sub PHILIPS_AUDIO_Notify +{ + my ($hash, $dev) = @_; + my $name = $hash->{NAME}; + return "" if ($dev->{NAME} ne "global"); + + my $events = deviceEvents($dev, AttrVal($name, "addStateEvent", 0)); + + return if(!$events); # Some previous notify deleted the array. + # + # Code to be executed on notify + # + return; +} -########################## sub PHILIPS_AUDIO_Attr { - my @a = @_; - my $hash = $defs{$a[1]}; - - if($a[0] eq "set" && $a[2] eq "disable") + my ($cmd, $name, $attrName, $attrVal) = @_; + my $hash = $defs{$name}; + + if($attrName eq "disable") { - if($a[3] eq "0") + if ($cmd eq "set") + { + $hash->{helper}{DISABLED} = $attrVal; + if ($attrVal eq "0") + { + PHILIPS_AUDIO_GetStatus($hash, 1); + } + } + else { $hash->{helper}{DISABLED} = 0; PHILIPS_AUDIO_GetStatus($hash, 1); } - elsif($a[3] eq "1") + } + elsif($attrName eq "maxListItems" && defined($attrVal)) + { + if ($cmd eq "set" && (($attrVal < 8) || ($attrVal > 999))) { - $hash->{helper}{DISABLED} = 1; + return "$attrName must be between 8 and 999"; } } - elsif($a[0] eq "del" && $a[2] eq "disable") + elsif($attrName eq "httpBufferTimeout" && defined($attrVal)) { - $hash->{helper}{DISABLED} = 0; - PHILIPS_AUDIO_GetStatus($hash, 1); + if ($cmd eq "set" && (($attrVal < 10) || ($attrVal > 15))) + { + return "$attrName must be between 10 and 15"; + } } - + elsif($attrName eq "requestTimeout" && defined($attrVal)) + { + if ($cmd eq "set" && (($attrVal < 10) || ($attrVal > 15))) + { + return "$attrName must be between 10 and 15"; + } + } + elsif($attrName eq "autoGetPresets" && defined($attrVal)) + { + if ($cmd eq "set" && (($attrVal < 0) || ($attrVal > 1))) + { + return "$attrName must be between 0 or 1"; + } + } + elsif($attrName eq "autoGetFavorites" && defined($attrVal)) + { + if ($cmd eq "set" && (($attrVal < 0) || ($attrVal > 1))) + { + return "$attrName must be between 0 or 1"; + } + } + elsif($attrName eq "playerBrowsingTimeout" && defined($attrVal)) + { + if ($cmd eq "set" && (($attrVal < 60) || ($attrVal > 600))) + { + return "$attrName must be between 60 or 600"; + } + } + # Start/Stop Timer according to new disabled-Value PHILIPS_AUDIO_ResetTimer($hash); return; } -############################# sub PHILIPS_AUDIO_Undefine { my($hash, $name) = @_; # Stop the internal GetStatus-Loop and exit - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "PHILIPS_AUDIO_GetStatus"); return; } -############################################################################################################ -# -# Begin of helper functions -# -############################################################################################################ +sub PHILIPS_AUDIO_SendCommandBuffer +{ + # Function called by an internal timer in case device busy + my ($hash) = @_; + + my $firstCommand = ""; + my ($url, $data, $cmd, $arg) = ("","","",""); + + + # Get first command from buffer + if + ( + @{$hash->{helper}{sendCommandBuffer}} + ) + { + $firstCommand = shift(@{$hash->{helper}{sendCommandBuffer}}); + + $url = $firstCommand->{url}; + $data = $firstCommand->{data}; + $cmd = $firstCommand->{cmd}; + $arg = $firstCommand->{arg}; + } + + # Only send in case buffer not empty + if(($url ne "") and ($cmd ne "") and ($arg ne "")) # $data may be empty + { + + $hash->{helper}{comeFromSendBuffer} = 1; + PHILIPS_AUDIO_SendCommand( + $hash, + $url, + $data, + $cmd, + $arg + ); + + if + ( + (@{$hash->{helper}{sendCommandBuffer}}) + ) + { + # Come back + # -> try again after 1 sec delay and process buffer. + RemoveInternalTimer($hash, "PHILIPS_AUDIO_SendCommandBuffer"); + InternalTimer(gettimeofday() + 1, "PHILIPS_AUDIO_SendCommandBuffer", $hash); + } + + } + else + { + # Do nothing. + # Reset flag in case buffer empty + $hash->{helper}{comeFromSendBuffer} = 0; + PHILIPS_AUDIO_ResetTimer($hash); + } + + return; +} -############################# -# sends a command to the receiver via HTTP sub PHILIPS_AUDIO_SendCommand { - my ($hash,$url,$data,$cmd,$arg) = @_; + my ($hash, $url, $data, $cmd, $arg) = @_; my $name = $hash->{NAME}; my $address = $hash->{IP_ADDRESS}; my $port = $hash->{PORT}; - - Log3 $name, 5, "PHILIPS_AUDIO ($name) - execute nonblocking \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" on $name: $data"; + + $hash->{helper}{networkRequest} = "idle" if(not defined($hash->{helper}{networkRequest})); # First run + $hash->{helper}{comeFromSendBuffer} = 0 if(not defined($hash->{helper}{comeFromSendBuffer})); # First run + + # $hash->{helper}{networkRequest} = "ready"; # !!!! Disable fhem side buffering !!! + + # buffer command and wait a second in case device busy + if ($hash->{helper}{networkRequest} eq "busy") + { + my $httpBufferData = + { + url => $url, + data => $data, + cmd => $cmd, + arg => $arg + }; + + # Append to buffer only if not coming from itself + # Or put to first position for resending + + # add remove start end + # push X X + # pop X X + # unshift X X + # shift X X + + if($hash->{helper}{comeFromSendBuffer} == 0) + { + push @{$hash->{helper}{sendCommandBuffer}}, $httpBufferData; + } + else + { + unshift @{$hash->{helper}{sendCommandBuffer}}, $httpBufferData; + } + + if($hash->{helper}{timeoutCounter} >= AttrVal($name, "httpBufferTimeout", 10)) + { + # X seconds timeout. Something went wrong. Clear buffer. Release "busy" + + # Delete all remaining commands + splice(@{$hash->{helper}{sendCommandBuffer}}); + + $hash->{helper}{timeoutCounter} = 0; + $hash->{helper}{networkRequest} = "idle"; + $hash->{helper}{comeFromSendBuffer} = 0; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "networkRequest", "idle"); + readingsBulkUpdate($hash, "networkError", "buffer timed out"); + readingsEndUpdate($hash, 1); + + Log3 $name, 3, "PHILIPS_AUDIO ($name) - HTTP buffer timeout. Please, check your network connection, ip-address etc. Device switched off?"; + + PHILIPS_AUDIO_ResetTimer($hash); + return; + } + + # -> try again after 1 sec delay and process buffer. + RemoveInternalTimer($hash, "PHILIPS_AUDIO_SendCommandBuffer"); + InternalTimer(gettimeofday() + 1, "PHILIPS_AUDIO_SendCommandBuffer", $hash); + + return; + } + else + { + if($url =~ /\/nav(.*)/) + { + $hash->{helper}{currentNavUrl} = $url; + } + else + { + $hash->{helper}{currentUrl} = $url; + } + + if(ReadingsVal($name, "presence", "absent") ne "absent") + { + $hash->{helper}{networkRequest} = "busy"; + readingsSingleUpdate($hash, "networkRequest", "busy", 1); + } + + PHILIPS_AUDIO_ResetTimer($hash); # getStatus + + } + + $hash->{helper}{timeoutCounter} = 0; + + Log3 $name, 5, "PHILIPS_AUDIO ($name) - Executing nonblocking \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" on $name: $data"; + + # Reset flag if successfully sent + $hash->{helper}{comeFromSendBuffer} = 0; + HttpUtils_NonblockingGet ({ - url => "http://".$address.":".$port."".$url, - timeout => AttrVal($name, "request-timeout", 30), - noshutdown => 1, - data => $data, - loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5), - hash => $hash, - cmd => $cmd, - arg => $arg, - callback => \&PHILIPS_AUDIO_ParseResponse - }); + url => "http://".$address.":".$port."".$url, + timeout => AttrVal($name, "requestTimeout", 10), + noshutdown => 1, + data => $data, + loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5), + hash => $hash, + cmd => $cmd, + arg => $arg, + method => "GET", + httpversion => "1.1", + keepalive => 1, # Philips app always uses keep-alive + header => $hash->{helper}{httpHeaderRefer}, + callback => \&PHILIPS_AUDIO_ParseResponse + }); + return; } -############################# -# parses the receiver response sub PHILIPS_AUDIO_ParseResponse { my ($param, $err, $data ) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; - my $cmd = $param->{cmd}; - my $arg = $param->{arg}; + my $cmd = $param->{cmd}; + my $arg = $param->{arg}; + + readingsBeginUpdate($hash); if(exists($param->{code})) { Log3 $name, 5, "PHILIPS_AUDIO ($name) - received HTTP code ".$param->{code}." for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\""; } - #Log3 $name, 5, "Error = $err"; - #Log3 $name, 5, "Data = $data"; - if($err ne "") { - Log3 $name, 5, "PHILIPS_AUDIO ($name) - could not execute command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $err"; - + + Log3 $name, 5, "PHILIPS_AUDIO ($name) - Could not execute command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $err"; + + # Release the flag + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + if((not exists($hash->{helper}{AVAILABLE})) or (exists($hash->{helper}{AVAILABLE}) and $hash->{helper}{AVAILABLE} == 1)) { - Log3 $name, 3, "PHILIPS_AUDIO ($name) - could not execute command on device $name. Please turn on your device in case of deactivated network standby or check for correct hostaddress."; - readingsSingleUpdate($hash, "presence", "absent", 1); - readingsSingleUpdate($hash, "state", "absent", 1); + Log3 $name, 3, "PHILIPS_AUDIO ($name) - Could not execute HTTP request. Please, check your network connection, ip-address etc. Device switched off? ($err)"; + readingsBulkUpdate($hash, "networkError", "$err"); + readingsBulkUpdate($hash, "presence", "absent"); + readingsBulkUpdate($hash, "state", "absent"); $hash->{STATE} = "absent"; - } - + } $hash->{helper}{AVAILABLE} = 0; + + $hash->{helper}{timeoutCounter} = 0; + $hash->{helper}{comeFromSendBuffer} = 0; + + # Close HTTP connection + HttpUtils_Close($param); + + # Force "home" state + $hash->{helper}{playerState} = "home"; + readingsBulkUpdate($hash, "playerState", "home"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + readingsEndUpdate($hash, 1); + + # Device firware "somehow buggy". Reanimate device after timeout. + # Go to /index + PHILIPS_AUDIO_SendCommand($hash, "/index", "","home", "noArg"); + + PHILIPS_AUDIO_ResetTimer($hash); + return; } elsif($data ne "") { Log3 $name, 5, "PHILIPS_AUDIO ($name) - got response for \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $data"; - + + delete $hash->{READINGS}{networkError}; + if (defined($hash->{helper}{AVAILABLE}) and $hash->{helper}{AVAILABLE} eq 0) { Log3 $name, 3, "PHILIPS_AUDIO ($name) - device $name reappeared"; - readingsSingleUpdate($hash, "presence", "present", 1); - } + readingsBulkUpdate($hash, "presence", "present"); + } $hash->{helper}{AVAILABLE} = 1; - - readingsBeginUpdate($hash); readingsBulkUpdate($hash, "power", "on"); readingsBulkUpdate($hash, "state","on"); @@ -597,8 +1144,18 @@ sub PHILIPS_AUDIO_ParseResponse { #readingsBulkUpdate($hash, "power", "on"); #readingsBulkUpdate($hash, "state","on"); - } - } + } + + } + elsif($cmd eq "home") + { + if($data =~ /'devicename'/) # Device responded correctly + { + $hash->{helper}{playerState} = "home"; + readingsBulkUpdate($hash, "playerState", "home"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + } + } elsif($cmd eq "mute") { if($data =~ /SUCCESS/) @@ -612,118 +1169,122 @@ sub PHILIPS_AUDIO_ParseResponse { readingsBulkUpdate($hash, "mute", "off"); } + } - elsif($cmd eq "removeFromFavourites") + elsif($cmd eq "favoriteRemove") { - # evtl. for future use + # replace \n by "" + $data =~ s/\n//g; + + if($data =~ /{'command':'MESSAGE','value':'(.+)'}/) + { + # Do nothing + } } - elsif($cmd eq "addToFavourites") + elsif($cmd eq "favoriteAdd") { - # evtl. for future use + # replace \n by "" + $data =~ s/\n//g; + + if($data =~ /{'command':'MESSAGE','value':'(.+)'}/) + { + # Do nothing + } } - elsif($cmd eq "inetRadioPreset") + elsif($cmd =~ m/^(selectPreset|inetRadioPreset)/) { # This command must be processed hierarchicaly through the navigation path if($hash->{helper}{cmdStep} == 1) { $hash->{helper}{cmdStep} = 2; + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; # Internet radio - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "inetRadioPreset", $hash->{helper}{inetRadioPreset}); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "selectPreset", $hash->{helper}{inetRadioPreset}); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; # Presets - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","inetRadioPreset", $hash->{helper}{inetRadioPreset}); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","selectPreset", $hash->{helper}{inetRadioPreset}); } elsif($hash->{helper}{cmdStep} == 3) { $hash->{helper}{cmdStep} = 4; + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + # Preset select - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioPreset})."\$1", "","inetRadioPreset", $hash->{helper}{inetRadioPreset}); - } - } - elsif($cmd eq "inetRadioPresetByName") - { + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioPreset})."\$1", "","selectPreset", $hash->{helper}{inetRadioPreset}); + $hash->{helper}{playerState} = "playing"; + readingsBulkUpdate($hash, "playerState", "playing"); + } + + } + elsif($cmd =~ m/^(selectFavorite|inetRadioFavorite)/) + { # This command must be processed hierarchicaly through the navigation path if($hash->{helper}{cmdStep} == 1) { $hash->{helper}{cmdStep} = 2; - # Internet radio - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "inetRadioPresetByName", $hash->{helper}{inetRadioPresetNumber}); - } - elsif($hash->{helper}{cmdStep} == 2) - { - $hash->{helper}{cmdStep} = 3; - # Presets - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","inetRadioPresetByName", $hash->{helper}{inetRadioPresetNumber}); - } - elsif($hash->{helper}{cmdStep} == 3) - { - $hash->{helper}{cmdStep} = 4; - # Preset select - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$". sprintf("%03d", $hash->{helper}{inetRadioPresetNumber}) . "\$1", "", "inetRadioPresetByName", $hash->{helper}{inetRadioPresetNumber}); - } - } - elsif($cmd eq "inetRadioFavorite") - { - # This command must be processed hierarchicaly through the navigation path - if($hash->{helper}{cmdStep} == 1) - { - $hash->{helper}{cmdStep} = 2; - # Internet radio favorite - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "inetRadioFavorite", $hash->{helper}{inetRadioFavorite}); + # Internet radio favorite# External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "selectFavorite", $hash->{helper}{inetRadioFavorite}); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + # Favorite Presets - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","inetRadioFavorite", $hash->{helper}{inetRadioFavorite}); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","selectFavorite", $hash->{helper}{inetRadioFavorite}); } elsif($hash->{helper}{cmdStep} == 3) { $hash->{helper}{cmdStep} = 4; + + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + # Favorite Preset select - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioFavorite})."\$1", "","inetRadioFavorite", $hash->{helper}{inetRadioFavorite}); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioFavorite})."\$1", "","selectFavorite", $hash->{helper}{inetRadioFavorite}); + $hash->{helper}{playerState} = "playing"; + readingsBulkUpdate($hash, "playerState", "playing"); } } - elsif($cmd eq "inetRadioFavoriteByName") - { - # This command must be processed hierarchicaly through the navigation path - if($hash->{helper}{cmdStep} == 1) - { - $hash->{helper}{cmdStep} = 2; - # Internet radio - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "", "inetRadioFavoriteByName", $hash->{helper}{inetRadioFavoriteNumber}); - } - elsif($hash->{helper}{cmdStep} == 2) - { - $hash->{helper}{cmdStep} = 3; - # Favorites - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","inetRadioFavoriteByName", $hash->{helper}{inetRadioFavoriteNumber}); - } - elsif($hash->{helper}{cmdStep} == 3) - { - $hash->{helper}{cmdStep} = 4; - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$". sprintf("%03d", $hash->{helper}{inetRadioFavoriteNumber}) . "\$1", "", "inetRadioFavoriteByName", $hash->{helper}{inetRadioFavoriteNumber}); - } - } elsif($cmd eq "play_pause") { if($data =~ /SUCCESS/) { - #readingsBulkUpdate($hash, "play_pause", "on"); - #readingsBulkUpdate($hash, "stop", "off"); - } + if(ReadingsVal($name, "playerPlaying", "") eq "no") + { + readingsBulkUpdate($hash, "playerPlaying", "yes"); + } + else + { + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsBulkUpdate($hash, "input", "-"); + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + } + } } elsif($cmd eq "stop") { if($data =~ /STOP/) { - readingsBulkUpdate($hash, "playing", "no"); + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsBulkUpdate($hash, "input", "-"); + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); } } - elsif($cmd eq "volume" or $cmd eq "volumeStraight") + elsif($cmd =~ m/^(volumeStraight|volumeUp|volumeDown)/) { if($data =~ /SUCCESS/) { @@ -731,43 +1292,260 @@ sub PHILIPS_AUDIO_ParseResponse my $targetVolume = $hash->{helper}{targetVolume}; readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $targetVolume)); } - } + } + elsif($cmd eq "elapse") + { + if($data =~ /'command':'(.+)',/) + { + if($1 eq "NOWPLAY") + { + # New player status information available + PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); + } + elsif($1 eq "ELAPSE") + { + + if($data =~ /'value':(.+),/) + { + # Sometimes the device does not refresh the ELAPSE -> NOWPLAY request + # Showing the current stream position. + # Check for a new song. + + $hash->{helper}{elapseValueOld} = "0" if(not defined($hash->{helper}{elapseValueOld})); + + if (int($1) < int($hash->{helper}{elapseValueOld})) # New song + { + PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); + } + + $hash->{helper}{elapseValueOld} = $1; + + if(int($1) < 3600) + { + readingsBulkUpdate($hash, "playerPlayTime", strftime("%M:\%S", gmtime($1))); + } + else + { + readingsBulkUpdate($hash, "playerPlayTime", strftime("\%H:\%M:\%S", gmtime($1))); + } + } + if($data =~ /'mute':(.+),/) + { + if(int($1) == 1) + { + readingsBulkUpdate($hash, "mute", "on"); + } + else + { + readingsBulkUpdate($hash, "mute", "off"); + } + } + if($data =~ /'shuffle':(.+),/) + { + if(int($1) == 1) + { + readingsBulkUpdate($hash, "playerShuffle", "on"); + } + else + { + readingsBulkUpdate($hash, "playerShuffle", "off"); + } + } + if($data =~ /'repeat':(.+),/) + { + if(int($1) == 0) + { + readingsBulkUpdate($hash, "playerRepeat", "off"); + } + elsif(int($1) == 1) + { + readingsBulkUpdate($hash, "playerRepeat", "single"); + } + elsif(int($1) == 2) + { + readingsBulkUpdate($hash, "playerRepeat", "all"); + } + } + if($data =~ /'rating':(.+),/) + { + readingsBulkUpdate($hash, "playerStreamRating", $1); + } + if($data =~ /'favstatus':(.+)}/) + { + if(int($1) == 1) + { + readingsBulkUpdate($hash, "playerStreamFavorite", "yes"); + } + else + { + readingsBulkUpdate($hash, "playerStreamFavorite", "no"); + } + } + if($data =~ /'volume':(.+),/) + { + readingsBulkUpdate($hash, "volumeStraight", $1); + readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $1)); + } + if($data =~ /'play':(.+),/) + { + if(int($1) == 1) + { + readingsBulkUpdate($hash, "playerPlaying", "yes"); + $hash->{helper}{playerState} = "playing"; + readingsBulkUpdate($hash, "playerState", "playing"); + } + else + { + readingsBulkUpdate($hash, "playerPlaying", "no"); + $hash->{helper}{playerState} = "home"; + readingsBulkUpdate($hash, "playerState", "home"); + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + #readingsBulkUpdate($hash, "input", "-"); + } + } + } + } + } elsif($cmd eq "nowplay") { - if($data =~ /'title':'\\'(.+)\\''/) + if($data =~ /'command':'(.+)',/) { - readingsBulkUpdate($hash, "title", PHILIPS_AUDIO_STREAMIUMNP2txt($1)); + if($1 eq "NOTHING") + { + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsBulkUpdate($hash, "playerState", "home"); + $hash->{helper}{playerState} = "home"; + } + } + elsif($data =~ /window.location = \"\/index\"/) + { + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + readingsBulkUpdate($hash, "playerPlaying", "no"); + readingsBulkUpdate($hash, "playerState", "home"); + $hash->{helper}{playerState} = "home"; } else { - readingsBulkUpdate($hash, "title", ""); + readingsBulkUpdate($hash, "playerPlaying", "yes"); + readingsBulkUpdate($hash, "playerState", "playing"); + $hash->{helper}{playerState} = "playing"; } - if($data =~ /'title':'(.+)'/) + if($data =~ /'devicename':'(.*?)'/) { - readingsBulkUpdate($hash, "title", PHILIPS_AUDIO_STREAMIUMNP2txt($1)); + if(not defined ($hash->{FRIENDLY_NAME})) + { + $hash->{FRIENDLY_NAME} = $1; + $hash->{helper}{dInfo}{FRIENDLY_NAME} = $1; + } + } + + if ($data =~ /'defaultAlbumArt':'res\/Internet_radio.jpg'/) + { + readingsBulkUpdate($hash, "input", "Internet Radio"); + } + + if($data =~ /'defaultAlbumArt':'res\/Media_library.jpg'/) + { + readingsBulkUpdate($hash, "input", "Media Library"); + } + + if($data =~ /'defaultAlbumArt':'res\/Home_AUX_nowplaying.jpg'/) + { + if($hash->{MODEL} eq "AW9000") + { + if($data =~ />Aux-inDigital-in 1(.+)Digital-in 2(.+)MP3-Link{READINGS}{playerRadioStationInfo}; + } + elsif(ReadingsVal($name, "input", "") eq "Internet Radio") + { + readingsBulkUpdate($hash, "playerRadioStationInfo", PHILIPS_AUDIO_html2txt($1)); + delete $hash->{READINGS}{playerTitle}; + } + } + elsif($data =~ /'title':'(.+)'/) + { + if((ReadingsVal($name, "input", "") eq "Media Library") or (ReadingsVal($name, "input", "") eq "Spotify")) + { + readingsBulkUpdate($hash, "playerTitle", PHILIPS_AUDIO_html2txt($1)); + delete $hash->{READINGS}{playerRadioStationInfo}; + } + elsif(ReadingsVal($name, "input", "") eq "Internet Radio") + { + readingsBulkUpdate($hash, "playerRadioStationInfo", PHILIPS_AUDIO_html2txt($1)); + delete $hash->{READINGS}{playerTitle}; + } } else { - readingsBulkUpdate($hash, "title", ""); + delete $hash->{READINGS}{playerTitle}; + delete $hash->{READINGS}{playerRadioStationInfo}; } if($data =~ /'subTitle':'(.+)'/) { - readingsBulkUpdate($hash, "subtitle", $1); + if((ReadingsVal($name, "input", "") eq "Media Library") or (ReadingsVal($name, "input", "") eq "Spotify")) + { + readingsBulkUpdate($hash, "playerAlbum", PHILIPS_AUDIO_html2txt($1)); + delete $hash->{READINGS}{playerRadioStation}; + } + else + { + readingsBulkUpdate($hash, "playerRadioStation", PHILIPS_AUDIO_html2txt($1)); + delete $hash->{READINGS}{playerAlbum}; + } } else { - readingsBulkUpdate($hash, "subtitle", ""); + delete $hash->{READINGS}{playerRadioStation}; + delete $hash->{READINGS}{playerAlbum}; } if($data =~ /'albumArt':'(.+)'/) { - readingsBulkUpdate($hash, "albumArt", $1); + readingsBulkUpdate($hash, "playerAlbumArt", $1); } else { - readingsBulkUpdate($hash, "albumArt", ""); + delete $hash->{READINGS}{playerAlbumArt}; } if($data =~ /'volume':(.+),/) @@ -783,27 +1561,39 @@ sub PHILIPS_AUDIO_ParseResponse if($data =~ /'elapsetime':(.+),/) { - readingsBulkUpdate($hash, "elapseTime", strftime("\%H:\%M:\%S", gmtime($1))); + if(int($1) < 3600) + { + readingsBulkUpdate($hash, "playerPlayTime", strftime("%M:\%S", gmtime($1))); + } + else + { + readingsBulkUpdate($hash, "playerPlayTime", strftime("\%H:\%M:\%S", gmtime($1))); + } } else { - readingsBulkUpdate($hash, "elapseTime", ""); + delete $hash->{READINGS}{playerPlayTime}; } + if($data =~ /'totaltime':(.+),/) { # Playing radio delivers that total time if($1 eq "65535") { - readingsBulkUpdate($hash, "totalTime", "infinite"); + delete $hash->{READINGS}{playerTotalPlayTime}; + } + elsif(int($1) < 3600) + { + readingsBulkUpdate($hash, "playerTotalPlayTime", strftime("%M:\%S", gmtime($1))); } else { - readingsBulkUpdate($hash, "totalTime", strftime("\%H:\%M:\%S", gmtime($1))); + readingsBulkUpdate($hash, "playerTotalPlayTime", strftime("\%H:\%M:\%S", gmtime($1))); } } else { - readingsBulkUpdate($hash, "totalTime", ""); + delete $hash->{READINGS}{playerTotalPlayTime}; } if($data =~ /'muteStatus':(.+),/) @@ -823,55 +1613,95 @@ sub PHILIPS_AUDIO_ParseResponse { if($1 == 1) { - readingsBulkUpdate($hash, "playing", "yes"); + readingsBulkUpdate($hash, "playerPlaying", "yes"); } else { - readingsBulkUpdate($hash, "playing", "no"); + readingsBulkUpdate($hash, "playerPlaying", "no"); + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + #readingsBulkUpdate($hash, "input", "-"); } } else { - readingsBulkUpdate($hash, "playing", "no"); + readingsBulkUpdate($hash, "playerPlaying", "no"); + delete $hash->{READINGS}{$_} foreach (grep /player/, keys %{$hash->{READINGS}}); + #readingsBulkUpdate($hash, "input", "-"); } } - - # Eventual future UPNP implementation. Requires IO::Socket::Multicast non-standard module. - elsif ($cmd eq "getModel") + elsif($cmd eq "homestatus") { + # Homestatus answers with + # {'command':'NOTHING',\n'value':0} + + # Do nothing ? + } + elsif ($cmd eq "getMediaRendererDesc") + { + + if($data =~ /(.+)<\/manufacturer>/) + { + $hash->{helper}{dInfo}{MANUFACTURER} = $1; + } + + if($data =~ /(.+)<\/manufacturerURL>/) + { + $hash->{helper}{dInfo}{MANUFACTURER_URL} = $1; + } + + if($data =~ /(.+)<\/manufacturerURL>/) + { + $hash->{helper}{dInfo}{MANUFACTURER_URL} = $1; + } + + if($data =~ /(.+)<\/presentationURL>/) + { + $hash->{helper}{dInfo}{PRESENTATION_URL} = $1; + } + + if($data =~ /(.+)<\/deviceType>/) + { + $hash->{helper}{dInfo}{UPNP_DEVICE_TYPE} = $1; + } + if($data =~ /(.+)<\/friendlyName>/) { - $hash->{FRIENDLY_NAME} = $1; + $hash->{helper}{dInfo}{FRIENDLY_NAME} = $1; } if($data =~ /(.+)<\/UDN>/) { - $hash->{UNIQUE_DEVICE_NAME} = uc($1); + my $uuid = uc($1); + $uuid =~ s/UUID://g; + $hash->{helper}{dInfo}{UUID} = $uuid; } - my $modelName = ""; - my $modelNumber = ""; + if($data =~ /(.+)<\/UPC>/) + { + $hash->{helper}{dInfo}{UPC} = $1; + } if($data =~ /(.+)<\/modelName>/) { - $modelName = $1; + $hash->{helper}{dInfo}{MODEL_NAME} = $1; } if($data =~ /(.+)<\/modelNumber>/) { - $modelNumber = $1; - } - # Combine both strings - if(($modelName ne "") and ($modelNumber ne "")) - { - $hash->{MODEL} = $modelName . $modelNumber; + $hash->{helper}{dInfo}{MODEL_NUMBER} = $1; } + + #if(($modelName ne "") and ($modelNumber ne "")) + #{ + # $hash->{MODEL} = $modelName . $modelNumber; + #} + if($data =~ /(.+)<\/serialNumber>/) { - $hash->{SERIAL_NUMBER} = uc($1); + $hash->{helper}{dInfo}{SERIAL_NUMBER} = uc($1); } if($data =~ /(.+)<\/modelDescription>/) { - $hash->{MODEL_DESCRIPTION} = $1; + $hash->{helper}{dInfo}{MODEL_DESCRIPTION} = $1; } # Replace \n, \r, \t from the string for XML parsing @@ -887,33 +1717,50 @@ sub PHILIPS_AUDIO_ParseResponse if($data =~ /(.+?)<\/iconList>/) { my $address = $hash->{IP_ADDRESS}; + my $port = ""; + + if ($hash->{MODEL} eq "NP3700") + { + $port = 7123; + } + elsif($hash->{MODEL} eq "NP3900") + { + $port = 49153; + } + elsif($hash->{MODEL} eq "AW9000") + { + $port = ""; + } + my $i = 1; while ($data =~ /(.+?)<\/url>/g) { - $hash->{"NP_ICON_$i"} = "http://".$address.":".$arg."/".$1; + # May have several urls according to the UPNP/DLNA standard + $hash->{helper}{dInfo}{"DEVICE_ICON_$i"} = "http://$address:$port/$1"; $i++; - } + } } } - elsif ($cmd eq "getInetRadioPresets") - { + elsif ($cmd eq "getPresets") + { + $hash->{helper}{TOTALINETRADIOPRESETS} = 0 if(not defined($hash->{helper}{TOTALINETRADIOPRESETS})); + # This command must be processed hierarchicaly through the navigation path if($hash->{helper}{cmdStep} == 1) { $hash->{helper}{cmdStep} = 2; # Internet radio - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getInetRadioPresets", "noArg"); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getPresets", "noArg"); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; # Presets - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","getInetRadioPresets", "noArg"); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","getPresets", "noArg"); } elsif($hash->{helper}{cmdStep} == 3) { - my $listedItems; # Visible Items in the display. Max 8 my $nextreqURL; my $i; @@ -925,9 +1772,9 @@ sub PHILIPS_AUDIO_ParseResponse { # In case on presets defined the player returns an Error # Do nothing - $hash->{helper}{TOTALINETRADIOPRESETS} = 0; - readingsBulkUpdate($hash, "totalInetRadioPresets", "0"); + readingsBulkUpdate($hash, "totalPresets", "0"); + delete $hash->{READINGS}{Reading_presets}; } else { @@ -940,7 +1787,7 @@ sub PHILIPS_AUDIO_ParseResponse if ($data =~ /'totalListCount':(.+),/) { $hash->{helper}{TOTALINETRADIOPRESETS} = $1; - readingsBulkUpdate($hash, "totalInetRadioPresets", $1); + readingsBulkUpdate($hash, "totalPresets", $1); #Log3 $name, 5, "ListedItems: $listedItems"; } @@ -949,65 +1796,59 @@ sub PHILIPS_AUDIO_ParseResponse # $listedItems = $1; #} - $data =~ s/\R//g; # Remove new lines - - for(;;) + $data =~ s/\R//g; # Remove new lines + + while ($data =~ /{'title':'(.+?)',/g) { - if($data =~ /{'title':'(.+?)',/g) - { - $presetName = $1; - #Log3 $name, 5, "PresetName: $presetName"; - - if($data =~ /'id':(.+?),/g) - { - $presetID = $1; - #Log3 $name, 5, "PresetIDLoop: $presetID"; - } - if ($presetID ne "" and $presetName ne "") - { - readingsBulkUpdate($hash, sprintf("inetRadioPreset%02d", $presetID), $presetName); - } - } - else + + $presetName = $1; + + if($data =~ /'id':(.+?),/g) { - last; - } + $presetID = $1; + #Log3 $name, 5, "PresetIDLoop: $presetID"; + } + if ($presetID ne "" and $presetName ne "") + { + readingsBulkUpdate($hash, sprintf(".inetRadioPreset_%02d", $presetID), $presetName); + } } #Log3 $name, 5, "PresetIDNachLoop: $presetID"; if($presetID < ($hash->{helper}{TOTALINETRADIOPRESETS})) # Maximum listed items = 8. Get the next items by sending the nextreqURL { - PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getInetRadioPresets", "noArg"); + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getPresets", "noArg"); } else { + readingsBulkUpdate($hash, "playerListStatus", "ready"); + delete $hash->{READINGS}{Reading_presets}; + delete $hash->{READINGS}{readingPresets}; $hash->{helper}{cmdStep} = 4; - if (exists($hash->{helper}{PRESETS})) - { - $hash->{helper}{PRESETS} = ""; # Reset the pull-down menue - } - PHILIPS_AUDIO_updatePresets($hash); - #my $debug = $hash->{helper}{PRESETS}; - #Log3 $name, 5, "Presets: $debug"; + # Finished } } - } - } - elsif ($cmd eq "getInetRadioFavorites") + } + } + elsif ($cmd eq "getFavorites") { + $hash->{helper}{TOTALINETRADIOFAVORITES} = 0 if(not defined($hash->{helper}{TOTALINETRADIOFAVORITES})); + # This command must be processed hierarchicaly through the navigation path if($hash->{helper}{cmdStep} == 1) { $hash->{helper}{cmdStep} = 2; # Internet radio - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getInetRadioFavorites", "noArg"); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$01\$001\$0", "","getFavorites", "noArg"); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; # Favorites - PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","getInetRadioFavorites", "noArg"); + PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","getFavorites", "noArg"); } elsif($hash->{helper}{cmdStep} == 3) { @@ -1016,7 +1857,8 @@ sub PHILIPS_AUDIO_ParseResponse # In case on presets defined the player returns an Error # Do nothing $hash->{helper}{TOTALINETRADIOFAVORITES} = 0; - readingsBulkUpdate($hash, "totalInetRadioFavorites", "0"); + readingsBulkUpdate($hash, "totalFavorites", "0"); + delete $hash->{READINGS}{Reading_favorites}; } else { @@ -1037,7 +1879,7 @@ sub PHILIPS_AUDIO_ParseResponse if ($data =~ /'totalListCount':(.+),/) { $hash->{helper}{TOTALINETRADIOFAVORITES} = $1; - readingsBulkUpdate($hash, "totalInetRadioFavorites", $1); + readingsBulkUpdate($hash, "totalFavorites", $1); #Log3 $name, 5, "ListedItems: $listedItems"; } @@ -1047,55 +1889,341 @@ sub PHILIPS_AUDIO_ParseResponse #} $data =~ s/\R//g; # Remove new lines - - for(;;) + + # Delete old readings + delete $hash->{READINGS}{$_} foreach (grep /.inetRadioFavorite_..$/, keys %{$hash->{READINGS}}); + + while($data =~ /{'title':'(.+?)',/g) { - if($data =~ /{'title':'(.+?)',/g) - { - $favoriteName = $1; - #Log3 $name, 5, "FavoriteName: $favoriteName"; + $favoriteName = $1; + #Log3 $name, 5, "FavoriteName: $favoriteName"; - if($data =~ /'id':(.+?),/g) - { - $favoriteID = $1; - #Log3 $name, 5, "FavoriteIDLoop: $favoriteID"; - } - if ($favoriteID ne "" and $favoriteName ne "") - { - readingsBulkUpdate($hash, sprintf("inetRadioFavorite%02d", $favoriteID), $favoriteName); - } - } - else + if($data =~ /'id':(.+?),/g) { - last; - } + $favoriteID = $1; + #Log3 $name, 5, "FavoriteIDLoop: $favoriteID"; + } + if ($favoriteID ne "" and $favoriteName ne "") + { + readingsBulkUpdate($hash, sprintf(".inetRadioFavorite_%02d", $favoriteID), $favoriteName); + } } #Log3 $name, 5, "FavoriteIDNachLoop: $favoriteID"; if($favoriteID < ($hash->{helper}{TOTALINETRADIOFAVORITES})) # Maximum listed items = 8. Get the next items by sending the nextreqURL { - PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getInetRadioFavorites", "noArg"); + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + + PHILIPS_AUDIO_SendCommand($hash, $nextreqURL, "","getFavorites", "noArg"); } else { + readingsBulkUpdate($hash, "playerListStatus", "ready"); + delete $hash->{READINGS}{Reading_favorites}; + delete $hash->{READINGS}{readingFavorites}; $hash->{helper}{cmdStep} = 4; - if (exists($hash->{helper}{FAVORITES})) - { - $hash->{helper}{FAVORITES} = ""; # Reset the pull-down menue - } - PHILIPS_AUDIO_updateFavorites($hash); - #my $debug = $hash->{helper}{FAVORITES}; - #Log3 $name, 5, "Favorites: $debug"; + # Finished } } + } + } + elsif($cmd =~ /input|selectStream/) + { + + $data =~ s/\R//g; # Remove new lines for regex + + if($arg eq "---") + { + # Do nothing } + elsif($arg =~ /internetRadio|onlineServices|mediaLibrary|(\d{3})_(.+?)_|lvl_(.+?)/) # 000_[c|i]_****** + { + # don't update menu content if playable item was chosen + if (defined $1) + { + if ($2 eq "i") + { + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + $hash->{helper}{playerState} = "playing"; + readingsBulkUpdate($hash, "playerState", "playing"); + # Stream selected. Manual operation finished. + $hash->{helper}{manualOperation} = 0; + readingsBulkUpdate($hash, ".manualOperation", "no"); + PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); + # Stream selected trigger getStatus + PHILIPS_AUDIO_ResetTimer($hash); # getStatus + return; + } + } + + if ($data =~ /{'command':'NOTHING','value':0}|Error<\/title>/) + { + my $errorMessage = "Player responded with unspecified error"; + + if($data =~ /alert\(\'(.*?)\'\);|\'command\':\'(.*?)\'/) + { + $errorMessage = $1; + } + + #Log3 $name, 3, "PHILIPS_AUDIO ($name) - Player response: Media Library change not successful ($errorMessage)."; + # Delete old readings + delete $hash->{READINGS}{$_} foreach (grep /listItem_...$/, keys %{$hash->{READINGS}}); + delete $hash->{READINGS}{$_} foreach (grep /.listItemTarget_...$/, keys %{$hash->{READINGS}}); + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + readingsBulkUpdate($hash, "listItem_001", "$errorMessage"); + readingsBulkUpdate($hash, ".listItemTarget_001", "$hash->{helper}{currentNavUrl}"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + + $hash->{helper}{manualOperation} = 0; + readingsBulkUpdate($hash, ".manualOperation", "no"); + + readingsEndUpdate($hash, 1); + + return "Player response: $errorMessage."; + } + + my $listDepthLevel = 0; + my $playerListTotalCount = 0; + my $listId = 0; + my $listItems = ""; + + if($data =~ /var listdetails = \{(.*)\};/) + { + my $listdetails_temp = $1; + my $listdetails = $listdetails_temp; + + if($listdetails =~ /'totalitems':(.*?),/) + { + readingsBulkUpdate($hash, ".listTotalItems", "$1"); + } + + $listdetails = $listdetails_temp; + + if($listdetails =~ /'totalListCount':(.*?),/) + { + $playerListTotalCount = $1; + $hash->{helper}{playerListTotalCount} = $1; + readingsBulkUpdate($hash, "playerListTotalCount", "$1"); + } + + $listdetails = $listdetails_temp; + + if($listdetails =~ /'depthlevel':(.*?),/) + { + $listDepthLevel = $1; + readingsBulkUpdate($hash, ".listDepthLevel", "$1"); + } + } + + if($data =~ /var listItem = \{(.*)\};/) + { + my $listItems_temp = $1; + $listItems = $listItems_temp; + + if($listItems =~ /'nextrequrl':'(.*?)'/) + { + $hash->{helper}{nextUrl} = $1; + readingsBulkUpdate($hash, ".listNextUrl", "$1"); + } + + $listItems = $listItems_temp; + + if($listItems =~ /'prevUrl':'(.*?)',/) + { + if($1 ne "") + { + $hash->{helper}{prevUrl} = $1; + readingsBulkUpdate($hash, ".listPrevUrl", "$1"); + } + else + { + $hash->{helper}{prevUrl} = "-"; + readingsBulkUpdate($hash, ".listPrevUrl", "-"); + } + } + + $listItems = $listItems_temp; + + if($listItems =~ /'items': \[(.*)\]/) + { + + # Delete old readings + delete $hash->{READINGS}{$_} foreach (grep /listItem_...$/, keys %{$hash->{READINGS}}); + delete $hash->{READINGS}{$_} foreach (grep /.listItemTarget_...$/, keys %{$hash->{READINGS}}); + + # Predefine all listItem readings with "reading..." + + for(my $i = 1; ($i < int($hash->{helper}{playerListTotalCount}) + 1) and ($i < AttrVal($name, "maxListItems", 100) + 1); $i++) + { + readingsBulkUpdate($hash, "listItem_".sprintf("%03s", $i), "reading..."); + } + + my $items = $1; + + while ($items =~ /\{(.*?)\}/g) + { + my $item = $1; + my $title = ""; + + if($item =~ /'title':'(.*?)',/) + { + $title = $1; + } + + if($item =~ /'id':(.*?),/) + { + $listId = $1; + } + + my $itemTarget = ""; + if($item =~ /'target':'(.*?)'/) + { + $itemTarget = $1; + readingsBulkUpdate($hash, ".listItemTarget_".sprintf("%03s", $listId), $1); + } + if(substr($itemTarget, -1) eq "1") + { + $title = "i_$title"; # item + } + else + { + $title = "c_$title"; # container + } + readingsBulkUpdate($hash, "listItem_".sprintf("%03s", $listId), PHILIPS_AUDIO_html2txt($title)); + } + } + } + if($listId < $hash->{helper}{playerListTotalCount}) + { + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + + PHILIPS_AUDIO_SendCommand($hash, "$hash->{helper}{nextUrl}", "", "selectStream", "list"); + } + else + { + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + } + } + elsif($arg =~ /list/) + { + if ($data =~ /{'command':'NOTHING','value':0}|<title>Error<\/title>/) + { + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + Log3 $name, 3, "PHILIPS_AUDIO ($name) - Player response: Media Library change not successful."; + return "Player response: Media Library change not successful."; + } + + $hash->{helper}{networkRequest} = "busy"; + readingsBulkUpdate($hash, "networkRequest", "busy"); + + $data =~ s/\R//g; # Remove new lines for regex + + my $listDepthLevel = 0; + my $playerListTotalCount = 0; + my $listId = 0; + my $listItems = ""; + + $listItems = $data; + + if($listItems =~ /'nextrequrl':'(.*?)'/) + { + $hash->{helper}{nextUrl} = $1; + readingsBulkUpdate($hash, ".listNextUrl", "$1"); + } + + $listItems = $data; + + if($listItems =~ /'prevUrl':'(.*?)',/) + { + if($1 ne "") + { + $hash->{helper}{prevUrl} = $1; + readingsBulkUpdate($hash, ".listPrevUrl", "$1"); + } + else + { + $hash->{helper}{prevUrl} = "-"; + readingsBulkUpdate($hash, ".listPrevUrl", "-"); + } + } + + $listItems = $data; + + if($listItems =~ /'items': \[(.*)\]/) + { + my $items = $1; + + while ($items =~ /\{(.*?)\}/g) + { + my $item_temp = $1; + my $item = $item_temp; + + my $title = ""; + if($item =~ /'title':'(.*?)',/) + { + $title = $1; + } + $item = $item_temp; + if($item =~ /'id':(.*?),/) + { + $listId = $1; + } + + $item = $item_temp; + my $itemTarget = ""; + if($item =~ /'target':'(.*?)'/) + { + $itemTarget = $1; + readingsBulkUpdate($hash, ".listItemTarget_".sprintf("%03s", $listId), $1); + } + + if(substr($itemTarget, -1) eq "1") + { + $title = "i_$title"; # item + } + else + { + $title = "c_$title"; # container + } + readingsBulkUpdate($hash, "listItem_".sprintf("%03s", $listId), PHILIPS_AUDIO_html2txt($title)); + + last if($listId eq AttrVal($name, "maxListItems", 100)); + } + } + + if(($listId < $hash->{helper}{playerListTotalCount}) && ($listId < AttrVal($name, "maxListItems", 100))) + { + # External Command. Not from buffer timer. + $hash->{helper}{fromSendCommandBuffer} = 0; + + PHILIPS_AUDIO_SendCommand($hash, "$hash->{helper}{nextUrl}", "", "selectStream", "list"); + } + else + { + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); + readingsBulkUpdate($hash, "playerListStatus", "ready"); + } + } + } - - readingsEndUpdate($hash, 1); - - return; + + $hash->{helper}{networkRequest} = "idle"; + readingsBulkUpdate($hash, "networkRequest", "idle"); } + + readingsEndUpdate($hash, 1); + + return; } ############################# @@ -1104,7 +2232,7 @@ sub PHILIPS_AUDIO_volume_rel2abs { my ($hash, $percentage) = @_; - return int(($percentage * 64 / 100 )); + return int($percentage * 64 / 100); } ############################# @@ -1113,7 +2241,7 @@ sub PHILIPS_AUDIO_volume_abs2rel { my ($hash, $absolute) = @_; - return int(int($absolute * 100) / int(64)); + return int($absolute * 100 / 64); } ############################# @@ -1122,21 +2250,21 @@ sub PHILIPS_AUDIO_ResetTimer { my ($hash, $interval) = @_; - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "PHILIPS_AUDIO_GetStatus"); if($hash->{helper}{DISABLED} == 0) { if(defined($interval)) { - InternalTimer(gettimeofday()+$interval, "PHILIPS_AUDIO_GetStatus", $hash, 0); + InternalTimer(gettimeofday() + $interval, "PHILIPS_AUDIO_GetStatus", $hash); } elsif((exists($hash->{READINGS}{presence}{VAL}) and $hash->{READINGS}{presence}{VAL} eq "present") and (exists($hash->{READINGS}{power}{VAL}) and $hash->{READINGS}{power}{VAL} eq "on")) { - InternalTimer(gettimeofday() + $hash->{helper}{ON_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash, 0); + InternalTimer(gettimeofday() + $hash->{helper}{ON_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash); } else { - InternalTimer(gettimeofday() + $hash->{helper}{OFF_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash, 0); + InternalTimer(gettimeofday() + $hash->{helper}{OFF_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash); } } return; @@ -1144,87 +2272,75 @@ sub PHILIPS_AUDIO_ResetTimer ############################# # convert all HTML entities into UTF-8 equivalent -sub PHILIPS_AUDIO_STREAMIUMNP2txt +sub PHILIPS_AUDIO_html2txt { my ($string) = @_; - $string =~ s/\\'//g; + $string =~ s/(&amp;quot;|&quot;|"|\\")/\"/g; + $string =~ s/(&amp;|&)/&/g; + $string =~ s/ / /g; + $string =~ s/('|\\'|\')/'/g; + $string =~ s/(\xe4|ä)/ä/g; + $string =~ s/(\xc4|Ä)/Ä/g; + $string =~ s/(\xf6|ö)/ö/g; + $string =~ s/(\xd6|Ö)/Ö/g; + $string =~ s/(\xfc|ü)/ü/g; + $string =~ s/(\xdc|Ü)/Ü/g; + $string =~ s/(\xdf|ß)/ß/g; + return $string; } -############################# -# Updates the pull-down menue -sub PHILIPS_AUDIO_updatePresets +sub PHILIPS_AUDIO_getMediaRendererDesc { + # queries the addition model descriptions my ($hash) = @_; + my $name = $hash->{NAME}; - - if((not defined($hash->{helper}{PRESETS}) or length($hash->{helper}{PRESETS}) == 0)) + + Log3 $name, 5, "PHILIPS_AUDIO ($name) - execute nonblocking \"MediaRendererDesc\""; + + my $url = ""; + my $port = ""; + + if ($hash->{MODEL} eq "NP3500") { - my $presetName; - - for(my $i = 1; $i < (AttrVal($name, "max-device-presets", "24") + 1); $i++) - { - if(exists($hash->{READINGS}{sprintf("inetRadioPreset%02d", $i)})) - { - if($i != 1) - { - $presetName = ","; - } - $presetName .= sprintf("%02d", $i) . ":"; - $presetName .= $hash->{READINGS}{sprintf("inetRadioPreset%02d", $i)}{VAL}; - $presetName =~ s/ /_/g; # Replace blank by underscore - $hash->{helper}{PRESETS} .= $presetName; - } - else - { - last; - } - } + # TBD + } + elsif($hash->{MODEL} eq "NP3700") + { + $url = "http://$hash->{IP_ADDRESS}:7123/DeviceDescription.xml"; + } + elsif(($hash->{MODEL} eq "NP3900") or ($hash->{MODEL} eq "AW9000")) + { + $url = "http://$hash->{IP_ADDRESS}:49153/nmrDescription.xml"; } else { - $hash->{helper}{PRESETS} = ""; + return "Unknown Device."; } - + + HttpUtils_NonblockingGet({ + url => $url, + timeout => AttrVal($name, "requestTimeout", 10), + noshutdown => 1, + data => "", + loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5), + hash => $hash, + cmd => "getMediaRendererDesc", + arg => "noArg", + callback => \&PHILIPS_AUDIO_ParseResponse + }); return; } -############################# -# Updates the pull-down menue -sub PHILIPS_AUDIO_updateFavorites -{ - my ($hash) = @_; - my $name = $hash->{NAME}; - - if((not defined($hash->{helper}{FAVORITES}) or length($hash->{helper}{FAVORITES}) == 0)) - { - my $favoriteName; - - for(my $i = 1; $i < (AttrVal($name, "max-device-favorites", "24") + 1); $i++) - { - if(exists($hash->{READINGS}{sprintf("inetRadioFavorite%02d", $i)})) - { - if($i != 1) - { - $favoriteName = ","; - } - $favoriteName .= sprintf("%02d", $i) . ":"; - $favoriteName .= $hash->{READINGS}{sprintf("inetRadioFavorite%02d" , $i)}{VAL}; - $favoriteName =~ s/ /_/g; # Replace blank by underscore - $hash->{helper}{FAVORITES} .= $favoriteName; - } - else - { - last; - } - } - } -} - 1; + =pod +=item device +=item summary controls a Philips Streamium Network Player in a local network +=item summary_DE steuert einen Philips Streamium Netzwerkplayer im lokalen Netzwerk =begin html <a name="PHILIPS_AUDIO"></a> @@ -1239,163 +2355,129 @@ sub PHILIPS_AUDIO_updateFavorites define <name> PHILIPS_AUDIO <device model> [<off_status_interval>] [<on_status_interval>] </code> <br><br> - This module controls a Philips Audio Player e.g. MCi, Streamium or Fidelio and (potentially) any other device including a navigation server.<br> - To check, open the following URL in the browser: http://[ip # of your device]:8889/index + This module controls a Philips Audio Player e.g. MCi, Streamium or Fidelio and potentially any other device controlled by the "myRemote" app.<br> + You might also check, opening the following URL in the browser: http://[ip number of your device]:8889/index <br><br> - (So far tested on: AW9000, NP3500, NP3700 and NP3900) + (Tested on: AW9000, NP3500, NP3700 and NP3900) <br><br> - Currently implemented features: - <br><br> - <ul> - <li>Power on/off</li> - <li>Internet Radio Preset Selection</li> - <li>Input selection</li> - <li>Volume +/-</li> - <li>Mute on/off</li> - <li>...</li> - </ul> - <br> - Defining a PHILIPS_AUDIO device will schedule an internal task (interval can be set - with optional parameters <off_status_interval> and <on_status_interval> in seconds.<br> - <off_status_interval> is a parameter used in case the device is powered off or not available.<br> - <on_status_interval> is a parameter used in case the device is powered on.<br> - If both parameters are unset, a default value 30 (seconds) for both is used.<br> - If <off_status_interval> is set only the same value is used for both parameters.<br> - Due to a relatively low-performance of the devices the minimum interval is set to 3 seconds. - <br> - The internal task periodically reads the status of the Network Player (power state, volume and mute status etc.) and triggers notify/filelog commands. - <br><br> Example:<br><br> - <ul><br> - Add the following code into the <b>fhem.cfg</b> configuration file and restart fhem:<br><br> + <ul> <code> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15<br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset<br><br> + define player PHILIPS_AUDIO NP3900 192.168.0.15<br><br> # With custom status interval of 60 seconds<br> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b><br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset<br><br> + define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b><br><br> # With custom "off"-interval of 60 seconds and "on"-interval of 10 seconds<br> define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60 10</b><br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset<br><br> </code> </ul> </ul> - <br><br> + <br> <a name="PHILIPS_AUDIOset"></a> <b>Set</b> <ul> <code> set <name> <command> [<parameter>] </code><br><br> - Currently, the following commands are defined.<br> <i>Note: Commands and parameters are case sensitive.</i><br> - <ul><br><br> - <u>Available commands:</u><br><br> - <li><b>aux</b>  -   Switches to the AUX input (MP3 Link or similar).</li> - <li><b>getInetRadioFavorites</b>   -   Reads the favorites. (May take some seconds...).</li> - <li><b>getInetRadioPresets</b>   -   Reads the presets. (May take some seconds...).</li> - <li><b>inetRadioFavorite</b> [1..24]   -   Selects an internet radio favorite (May take some seconds...).</li> - <li><b>inetRadioPreset</b> [1..24]   -   Selects an internet radio preset (May take some seconds...).</li> - <li><b>inetRadioFavoriteByName</b> [name]   -   Selects an internet radio favorite by name. (getInetRadioFavorites must be run previously for the command to become available.) (May take some seconds...).</li> - <li><b>inetRadioPresetByName</b> [name]   -   Selects an internet radio preset by name. (getInetRadioPresets must be run previously for the command to become available.) (May take some seconds...).</li> - <li><b>mute</b>  -   Mutes the device.</li> - <li><b>unmute</b>  -   Unmutes the device.</li> - <li><b>next</b>   -   Selects the next song, preset etc.</li> - <li><b>play_pause</b>   -   Toggles PLAY/PAUSE.</li> - <li><b>previous</b>   -   Selects the previous song, preset etc.</li> - <li><b>repeat [single|all|off]</b>   -   Sets the repeat mode.</li> - <li><b>shuffle [on|off]</b>   -   Sets the shuffle mode.</li> - <li><b>standbyButton</b>   -   Toggles between standby and power on.</li> - <li><b>statusRequest</b>   -   Updates the readings.</li> - <li><b>stop</b>   -   Stops the player.</li> - <li><b>volume</b>   -   Sets the relative volume 0...100%.</li> - <li><b>volumeStraight</b>   -   Sets the absolute volume 0...64.</li> - </ul><br><br> - - A typical example is powering the device remotely and tuning the favourite radio station:<br><br> - Add the following code into the <b>fhem.cfg</b> configuration file:<br><br><br> - <ul> - <code> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 30 5<br> - attr PHAUDIO_player webCmd volume:mute:inetRadioPreset - </code> - </ul><br><br> - Add the following code into the <b>99_MyUtils.pm</b> file:<br><br> - <ul> - <code> - sub startMyFavouriteRadioStation()<br> - {<br> -   fhem "set PHAUDIO_player inetRadioPreset 1";<br> -   sleep 1;<br> -   fhem "set PHAUDIO_player volume 30";<br> - } - </code> - </ul> - <br><br> - It's a good idea to insert a 'sleep' instruction between each fhem commands due to internal processing time of the player. Be patient when executing the commands...<br><br> - - Now the function can be called by typing the following line in the FHEM command line or by the notify-definitions:<br><br> - <ul> - <code> - {startMyFavouriteRadioStation()}<br><br> - </code> + <ul><br> + <li><b>favoriteAdd</b> – Adds currently played Internet Radio stream to favorites</li> + <li><b>favoriteRemove</b> – Removes currently played Internet Radio stream from favorites</li> + <li><b>getFavorites</b> – Reads stored favorites from the device (may take some time...)</li> + <li><b>getMediaRendererDesc</b> – Reads device specific information (stored in the deviceInfo reading)</li> + <li><b>getPresets</b> – Reads stored presets from the device (may take some time...)</li> + <li><b>input</b> – Selects the following input</li> + <ul> + <li><b>analogAux</b> – Selects the analog AUX input (AW9000 only)</li> + <li><b>digital1Coaxial</b> – Selects the digital coaxial input (AW9000 only)</li> + <li><b>digital2Optical</b> – Selects the digital optical input (AW9000 only)</li> + <li><b>internetRadio</b> – Selects the Internet Radio input</li> + <li><b>mediaLibrary</b> – Selects the Media Library input (UPnP/DLNA server) (not available on AW9000)</li> + <li><b>mp3Link</b> – Selects the analog MP3 Link input (not available on AW9000)</li> + <li><b>onlineServices</b> – Selects the Online Services input</li> + </ul> + <li><b>mute [ on | off ]</b> – Mutes/unmutes the device</li> + <li><b>player</b> – Player related commands</li> + <ul> + <li><b>next</b> – Selects next audio stream</li> + <li><b>prev</b> – Selects previous audio stream</li> + <li><b>play-pause</b> – Plays/pauses the current audio stream</li> + <li><b>stop</b> – Stops the current audio stream</li> + </ul> + <li><b>repeat [ single | all | off]</b> – Selects the repeate mode</li> + <li><b>selectFavorite [ name ]</b> – Selects a favorite. Empty if no favorites found. (see also getFavorites)</li> + <li><b>selectFavoriteByNumber [ number ]</b> – Selects a favorite by its number. Empty if no favorites found. (see also getFavorites)</li> + <li><b>selectPreset [ name ]</b> – Selects a preset. Empty if no presets found. (see also getPresets)</li> + <li><b>selectPresetByNumber [ number ]</b> – Selects a preset by its number. Empty if no presets found. (see also getPresets)</li> + <li><b>selectStream [ name ]</b> – Context-sensitive. Selects a stream depending on the current input and player list content</li> + <li><b>shuffle [ on | off ]</b> – Sets the shuffle mode</li> + <li><b>standbyButton</b> – Emulates the standby button. Toggles between standby and power on</li> + <li><b>volume</b> – Sets the relative volume 0...100%</li> + <li><b>volumeDown</b> – Sets the device specific volume by one decrement</li> + <li><b>volumeStraight</b> – Sets the device specific absolute volume 0...64</li> + <li><b>volumeUp</b> – Sets the device specific volume by one increment</li> </ul> </ul> + <br> <a name="PHILIPS_AUDIOget"></a> <b>Get</b> <ul> <code> - get <name> <reading> + get <name> <reading> <reading name> </code> - <br><br> - Currently, the 'get' command returns reading values only. For a specific list of possible values, see section <b>"Generated Readings"</b>.<br><br> + <ul> + <br> + <li><b>deviceInfo</b> – Returns device specific information</li> + <li><b>reading</b></li> + <ul> + <li><b>input</b> – Returns current input or '-' if not playing</li> + <li><b>listItem_xxx</b> – Returns player list item (limited to 999 entries)</li> + <li><b>networkError</b> – Shows an occured current network error</li> + <li><b>networkRequest</b> – Shows current network activity (idle/busy)</li> + <li><b>power</b> – Returns power status (on/off)</li> + <li><b>playerAlbum</b> – Returns the album name of played stream</li> + <li><b>playerAlbumArt</b> – Returns the album art of played audio stream</li> + <li><b>playerListStatus</b> – Returns current player list status (busy/ready)</li> + <li><b>playerListTotalCount</b> – Returns number of player list entries</li> + <li><b>playerPlayTime</b> – Returns audio stream duration</li> + <li><b>playerPlaying</b> – Returns current player playing state (yes/no)</li> + <li><b>playerRadioStation</b> – Returns the name of played radio station</li> + <li><b>playerRadioStationInfo</b> – Returns additional info of the played radio station</li> + <li><b>playerRepeat</b> – Returns current repeat mode (off/single/all)</li> + <li><b>playerShuffle</b> – Returns current shuffle mode (on/off)</li> + <li><b>playerState</b> – Returns current player state (home/browsing/playing)</li> + <li><b>playerStreamFavorite</b> – Shows if audio stream is a favorite (yes/no)</li> + <li><b>playerStreamRating</b> – Shows rating of the audio stream</li> + <li><b>playerTitle</b> – Returns audio stream's title</li> + <li><b>playerTotalTime</b> – Shows audio stream's total time</li> + <li><b>presence</b> – Returns peresence status (present/absent)</li> + <li><b>state</b> – Returns current state (on/off)</li> + <li><b>totalFavorites</b> – Returns total number of stored favorites (see getFavorites)</li> + <li><b>totalPresets</b> – Returns total number of stored presets (see getPresets)</li> + <li><b>volume</b> – Returns current relative volume (0...100%)</li> + <li><b>volumeStraight</b> – Returns current device absolute volume (0...64)</li> + </ul> + </ul> + <br> </ul> - <a name="PHILIPS_AUDIOattr"></a> + <a name="PHILIPS_AUDIOattr"></a> <b>Attributes</b><br><br> <ul> <ul> - <li><b><a href="#do_not_notify">do_not_notify</a></b></li> - <li><b><a href="#readingFnAttributes">readingFnAttributes</a></b></li> - <li><b><a name="request-timeout">request-timeout</a></b></li> - <br>Optional attribute change the response timeout in seconds for all queries to the receiver. - <br>Possible values: 1...5 seconds. Default value is 4 seconds.<br><br> - <li><b><a name="max-device-presets">max-device-presets</a></b></li> - <br>Optional attribute defining maximum number of available preset slots in device's memory. Default value 24.<br><br> - <li><b><a name="max-device-favorites">max-device-favorites</a></b></li> - <br>Optional attribute defining maximum number of favorite slots in device's memory. Default value 24.<br><br> - <li><b><a name="disable">disable</a></b></li> - <br>Optional attribute to disable the internal cyclic status update of the receiver. Manual status updates via statusRequest command is still possible. - <br>Possible values: 0 → perform cyclic status update, 1 → don't perform cyclic status updates.<br><br><br> + <li><b>autoGetFavorites</b> – Automatically read favorites from device if none available (default off)</li> + <li><b>autoGetPresets</b> – Automatically read presets from device if none available (default off)</li> + <li><b>do_not_notify</b></li> + <li><b>httpBufferTimeout</b> – Optional attribute defing the internal http buffer timeount (default 10)</li> + <li><b>maxListItems</b> – Defines max. number of player list items (default 100)</li> + <li><b>playerBrowsingTimeout</b> – Defines the inactivity timeout for browsing. After that timeout the player returns to the state 'home' in which the readings are updated automaically again. (default 180 seconds)</li> + <li><b>readingFnAttributes</b></li> + <li><b>requestTimeout</b> – Optional attribute defining the http response timeout (default 4 seconds)</li> </ul> </ul> - <b>Readings</b><br><br> - <ul> - <ul> - <li><b>albumArt</b> - Link to current album art or radio station.</li> - <li><b>elapseTime</b> - Elapse time of the played audio.</li> - <li><b>inetRadioFavorite[value]</b> - Name of the stored favorite.</li> - <li><b>inetRadioPreset[value]</b> - Name of the stored preset.</li> - <li><b>mute</b> - Reports the mute status (on|off).</li> - <li><b>playing</b> - Reports the current playier status (yes|no).</li> - <li><b>power</b> - Reports the current power status (on|absent).</li> - <li><b>presence</b> - Reports the current presence (present|absent).</li> - <li><b>state</b> - Reports the current state status (on|absent).</li> - <li><b>subtitle</b> - Reports the current subtitle of played audio.</li> - <li><b>totalInetRadioFavorites</b> - Number of stored favorites.</li> - <li><b>totalInetRadioPresets</b> - Number of stored presets.</li> - <li><b>title</b> - Reports the current title of played audio.</li> - <li><b>totalTime</b> - Reports the total time of the played audio.</li> - <li><b>volume</b> - Reports current relative volume (0..100).</li> - <li><b>volumeStraight</b> - Reports current absolute volume (0..64).</li> - </ul> - </ul><br> - <b>Implementer's note</b><br><br> - <ul> - Trivial: In order to use that module the network player must be connected to the Ethernet.<br> - There's no possibility to read back the current power on/standby status from the device. This fuctionality is missing in the server application.<br> - </ul><br> + <br> </ul> + =end html + =begin html_DE <a name="PHILIPS_AUDIO"></a> @@ -1409,46 +2491,27 @@ sub PHILIPS_AUDIO_updateFavorites define <name> PHILIPS_AUDIO <device model> [<off_status_interval>] [<on_status_interval>] </code> <br><br> - Mit Hilfe dieses Moduls lassen sich Philips Audio Netzwerk Player wie z.B. MCi, Streamium oder Fidelio via Ethernet steuern.<br> - Theoretisch sollten alle Geräte, die über einer implementierten HTTP Server am Port 8889 haben (http://[ip Nummer des Gerätes]:8889/index), bedient werden können.<br> - <br> + Mit Hilfe dieses Moduls lassen sich Philips Audio Netzwerk Player wie z.B. MCi, Streamium oder Fidelio im lokalen Netzwerk steuern.<br> + Geräte, die über die myRemote App oder einen internen HTTP Server am Port 8889 sollten theoretisch ebenfalls bedient werden können.<br> + (http://[ip Nummer des Gerätes]:8889/index)<br> <br> (Getestet mit: AW9000, NP3500, NP3700 und NP3900) - <br> - <br> - Die aktuelle Implementierung ermöglicht u.a. den folgenden Funktionsumfang:<br><br> - <ul> - <li>Power on/off</li> - <li>Internet Radio Preset Auswahl</li> - <li>Input Auswahl</li> - <li>Volume +/-</li> - <li>Mute on/off</li> - <li>...</li> - </ul> - <br> - Eine PHILIPS_AUDIO Definition initiiert einen internen Task, der von FHEM zyklisch abgearbeitet wird.<br> - Das Intervall (in Sekunden) kann für die Zustände <on_status_interval> und <off_status_interval> optional gesetzt werden.<br> - <off_status_interval> steht für das Intervall, wenn das Gerät ausgeschaltet/abwesend ist.<br> - <on_status_interval> steht für das Intervall, wenn das Gerät eingeschaltet/verfügbar ist.<br> - Wenn keine Parameter angegeben wurden, wird ein Default-Wert von 30 Sekunden für beide gesetzt.<br> - Wenn nur <off_status_interval> gesetzt wird, gilt dieser Wert für beide Zustände (eingeschaltet/ausgeschaltet).<br> - Der Task liest zyklisch grundlegende Parameter vom Player und triggert notify/filelog Befehle.<br> - Aufgrund der recht schwachen Rechenleistung der Player wurde das minimale Intervall auf 3 Sekunden beschränkt.<br><br> - Beispiel:<br><br> + <br><br> + Beispiel:<br> <ul><br> - Definition in der <b>fhem.cfg</b> Konfigurationsdatei:<br><br> <code> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15<br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset<br><br> + define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 + <br> + <br> # 60 Sekunden Intervall<br> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b><br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset<br><br> + define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60</b> + <br> + <br> # 60 Sekunden Intervall für "off" und 10 Sekunden für "on"<br> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60 10</b><br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset - </code> + define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 <b>60 10</b> + </code> </ul> - </ul><br><br> + </ul><br> <a name="PHILIPS_AUDIOset"></a> <b>Set</b> <ul> @@ -1456,108 +2519,103 @@ sub PHILIPS_AUDIO_updateFavorites set <name> <command> [<parameter>] </code> <br><br> - Aktuell sind folgende Befehle implementiert:<br> - <i>Bemerkung: Bitte bei den Befehlen und Parametern die Groß- und Kleinschreibung beachten.</i><br> - <ul><br><br> - <li><b>aux</b>  -   Schaltet auf den AUX Eingang um (MP3 Link oder ähnlich.).</li> - <li><b>getInetRadioFavorites</b>   -   Liest die Favoriten. (Kann einige Sekunden dauern...).</li> - <li><b>getInetRadioPresets</b>   -   Liest die Presets. (Kann einige Sekunden dauern...).</li> - <li><b>inetRadioFavorite</b> [1..24]   -   Wählt den Internetradio-Lieblingssender (Das Umschalten kann einige Sekunden dauern...).</li> - <li><b>inetRadioPreset</b> [1..24]   -   Wählt die Internetradio Voreinstellung (Das Umschalten kann einige Sekunden dauern...).</li> - <li><b>inetRadioFavoriteByName</b> [name]   -   Wählt einen Internetradio Favoriten mit Hilfe seines Names. (getInetRadioFavorites muss zuvor ausgeführt werden, damit der Befehl verfügbar wird. (Kann einige Sekunden dauern...).</li> - <li><b>inetRadioPresetByName</b> [name]   -   Wählt eine Internetradio Voreinstellung mit Hilfe ihres Names. (getInetRadioPresets muss zuvor ausgeführt werden, damit der Befehl verfügbar wird. (Kann einige Sekunden dauern...).</li> - <li><b>mute</b>  -   Stummschaltung des Players.</li> - <li><b>unmute</b>  -   Deaktivierung der Stummschaltung.</li> - <li><b>next</b>   -   Wählt den nächten Titel, Voreinstellung etc.</li> - <li><b>play_pause</b>   -   Schaltet um zwischen PLAY um PAUSE.</li> - <li><b>previous</b>   -   Wählt den vorherigen Titel, Voreinstellung etc.</li> - <li><b>repeat [single|all|off]</b>   -   Bestimmt den Wiederholungsmodus.</li> - <li><b>shuffle [on|off]</b>   -   Bestimmt den Zufallswiedergabemodus.</li> - <li><b>standbyButton</b>   -   Schaltet um zwischen Standby und Power on.</li> - <li><b>statusRequest</b>   -   Readings Update.</li> - <li><b>stop</b>   -   Stoppt den Player.</li> - <li><b>volume</b>   -   Setzt die relative Lautstärke 0...100%.</li> - <li><b>volumeStraight</b>   -   Setzt die absolute Lautstärke 0...64.</li> - </ul><br><br> - Ein typisches Beispiel ist das Einschalten des Gerätes und das Umschalten auf den Lieblingsradiosender:<br><br> - Beispieldefinition in der <b>fhem.cfg</b> Konfigurationsdatei:<br><br><br> + <i>Bemerkung: Befehle und Parameter sind case-sensitive</i><br> + <ul><br> + <li><b>favoriteAdd</b> – Fügt den aktuellen Audiostream zu Favoriten hinzu</li> + <li><b>favoriteRemove</b> – Löscht den aktuellen Audiostream aus den Favoriten</li> + <li><b>getFavorites</b> – Liest aus die gespeicherten Favoriten aus dem Gerät (kann einige Zeit dauern...)</li> + <li><b>getMediaRendererDesc</b> – Liest aus Gerätspezifische Informationen aus (siehe auch deviceInfo reading)</li> + <li><b>getPresets</b> – Liest aus die gespeicherten Presets aus dem Gerät (kann einige Zeit dauern...)</li> + <li><b>input</b> – Schaltet auf den folgenden Eingang</li> <ul> - <code> - define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 30 5<br> - attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset - </code> - </ul><br><br> - Folgender Code kann anschließend in die Datei <b>99_MyUtils.pm</b> eingebunden werden:<br><br> + <li><b>analogAux</b> – AUX input (nur AW9000)</li> + <li><b>digital1Coaxial</b> – digital coaxial input (nur AW9000)</li> + <li><b>digital2Optical</b> – digital optical input (nur AW9000)</li> + <li><b>internetRadio</b> – Internet Radio</li> + <li><b>mediaLibrary</b> – Media Library (UPnP/DLNA server) (nicht verfügbar beim AW9000)</li> + <li><b>mp3Link</b> – Analoger MP3 Link (nicht verfügbar beim AW9000)</li> + <li><b>onlineServices</b> – Online Services</li> + </ul> + <li><b>mute [ on | off ]</b> – Stummschaltung (an/aus)</li> + <li><b>player</b> – Player-Befehle</li> <ul> - <code> - sub startMyFavouriteRadioStation()<br> - {<br> -   fhem "set PHAUDIO_player inetRadioPreset 1";<br> -   sleep 1;<br> -   fhem "set PHAUDIO_player volume 30";<br>} - </code> - </ul><br><br> - <i>Bemerkung: Aufgrund der relativ langsamen Befehlsverarbeitung im Player im Vergleich zur asynchronen Ethernet-Kommunikation, kann es vorkommen, dass veraltete Statusinformationen zurückgesendet werden.<br> - Aus diesem Grund wird empfohlen, während der Automatisierung zwischen den 'set' und 'get' Befehlen ein Delay einzubauen.</i><br><br> - Die Funktion kann jetzt in der FHEM Befehlszeile eingegeben oder in die Notify-Definitionen eingebunden werden.<br><br> + <li><b>next</b> – Nächstee Audiostream</li> + <li><b>prev</b> – Letzter Audiostream</li> + <li><b>play-pause</b> – Play/pause des aktuellen Audiostreams</li> + <li><b>stop</b> – Stoppt das Abspielen des aktuellen Audiostreams</li> + </ul> + <li><b>repeat [ single | all | off]</b> – Stellt den repeat mode ein</li> + <li><b>selectFavorite [ name ]</b> – Wählt einen Favoriten. Leer falls keine Favoriten vorhanden (s. getFavorites)</li> + <li><b>selectFavoriteByNumber [ number ]</b> – Wählt einen Favoriten anhand seiner Speichernummer. Leer falls keine Favoriten vorhanden (s. getFavorites)</li> + <li><b>selectPreset [ name ]</b> – Wählt einen Preset. Leer falls keine Presets vorhanden (s. getPresets)</li> + <li><b>selectPresetByNumber [ number ]</b> – Wählt einen Preset anhand seiner Speichernummer. Leer falls keine Presets vorhanden (see also getPresets)</li> + <li><b>selectStream [ name ]</b> – Context-sensitive. Wählt einen Audiostream. Hängt vom aktuellen Inhalt der Playerlist ab.</li> + <li><b>shuffle [ on | off ]</b> – Wählt den gewünschten Shuffle Modus</li> + <li><b>standbyButton</b> – Emuliert den standby-Knopf. Toggelt zwischen standby und power on</li> + <li><b>volume</b> – Setzt die relative Lautstärke 0...100%</li> + <li><b>volumeDown</b> – Setzt die Lautstärke um ein Dekrement herunter</li> + <li><b>volumeStraight</b> – Setzt die devicespezifische Lautstärke 0...64</li> + <li><b>volumeUp</b> – Setzt die Lautstärke um ein Inkrement herauf</li> + </ul> + </ul> + <br> + <a name="PHILIPS_AUDIOget"></a> + <b>Get</b> + <ul> + <code> + get <name> <reading> <reading name> + </code> + <ul> + <br> + <li><b>deviceInfo</b> – Liefert devicespezifische Information</li> + <li><b>reading</b></li> <ul> - <code> - {startMyFavouriteRadioStation()}<br><br> - </code> + <li><b>input</b> – Liefert den aktuellen Eingang oder '-' falls kein Audiostream aktiv</li> + <li><b>listItem_xxx</b> – Liefert Einträge der Playerliste (limitiert auf 999 Einträge)</li> + <li><b>networkError</b> – Liefert einen potentiellen Netzwerkfehler</li> + <li><b>networkRequest</b> – Liefert die aktuelle Netzwerkaktivität (idle/busy)</li> + <li><b>power</b> – Liefert den Power-Status (on/off)</li> + <li><b>playerAlbum</b> – Liefert den Albumnamen des aktiven Audiostreams</li> + <li><b>playerAlbumArt</b> – Liefert die Albumart des aktiven Audiostreams</li> + <li><b>playerListStatus</b> – Liefert den aktuellen Zusatand der Playlist (busy/ready)</li> + <li><b>playerListTotalCount</b> – Liefert die Anzahl der Playlisteinträge</li> + <li><b>playerPlayTime</b> – Liefert die aktuell Audiostreamspieldauer</li> + <li><b>playerPlaying</b> – Zeigt an, ob Audiostream abgespielt wird (yes/no)</li> + <li><b>playerRadioStation</b> – Liefert den Stationsnamen des Radiosenders</li> + <li><b>playerRadioStationInfo</b> – Liefert zusätzliche Informationen des Radiosenders</li> + <li><b>playerRepeat</b> – Zeigt den Reapeat Mode an (off/single/all)</li> + <li><b>playerShuffle</b> – Zeigt den aktuellen Shuffle mode an (on/off)</li> + <li><b>playerState</b> – Zeigt den Playerzustand an (home/browsing/playing)</li> + <li><b>playerStreamFavorite</b> – Zeigt an, ob aktueller Audiostream ein Favorit ist (yes/no)</li> + <li><b>playerStreamRating</b> – Zeigt das rating des Audiostreams</li> + <li><b>playerTitle</b> – Zeigt den Titel des Audiostreams an</li> + <li><b>playerTotalTime</b> – Zeigt die Audiostreamdauer an</li> + <li><b>presence</b> – Liefert den presence status (present/absent)</li> + <li><b>state</b> – Lifert den aktuellen Gerätestatus (on/off)</li> + <li><b>totalFavorites</b> – Liefert die Anzahl gepseicherter Favoriten (s. getFavorites)</li> + <li><b>totalPresets</b> – Liefert die Anzahl gepseicherter Presets (see getPresets)</li> + <li><b>volume</b> – Liefert die relative Lutstärke (0...100%)</li> + <li><b>volumeStraight</b> – Liefert die devicespezifische Lautstärke (0...64)</li> </ul> </ul> - <a name="PHILIPS_AUDIOget"></a> - <b>Get</b> - <code> - get <name> <reading> - </code><br><br> - Aktuell liefert der Befehl 'get' ausschließlich Reading-Werte (s. Abschnitt <b>"Readings"</b>).<br><br> + <br> </ul> <a name="PHILIPS_AUDIOattr"></a> - <ul> <b>Attribute</b><br><br> <ul> <ul> - <li><b><a href="#do_not_notify">do_not_notify</a></b></li> - <li><b><a href="#readingFnAttributes">readingFnAttributes</a></b></li><br> - <li><b><a name="request-timeout">request-timeout</a></b></li><br> - Optionales Attribut, um das HTTP response timeout zu beeinflußen.<br> - Mögliche Werte: 1...5 Sekunden. Default Wert ist 4 Sekunden.<br><br> - <li><b><a name="max-device-presets">max-device-presets</a></b></li> - <br>Optionales Attribut zur Bestimmung der max. Anzahl von verfügbaren Preset-Speicherplätzen des Players. Default-Wert 24.<br><br> - <li><b><a name="max-device-favorites">max-device-favorites</a></b></li> - <br>Optionales Attribut zur Bestimmung der max. Anzahl von verfügbaren Favorite-Speicherplätzen des Players. Default-Wert 24.<br><br> - <li><b><a name="disable">disable</a></b></li><br> - Optionales Attribut zum Deaktivieren des internen zyklischen Timers zum Aktualisieren des NP-Status. Manuelles Update ist nach wie vor möglich.<br> - Mögliche Werte: 0 → Zyklisches Update aktiv., 1 → Zyklisches Update inaktiv.<br><br><br> + <li><b>autoGetFavorites</b> – Automatisches Auslesen der Favoriten beim Modulstart falls keine vorhanden (default off)</li> + <li><b>autoGetPresets</b> – Automatisches Auslesen der Presets beim Modulstart falls keine vorhanden (default off)</li> + <li><b>do_not_notify</b></li> + <li><b>httpBufferTimeout</b> – Optionalles Attribut für den internen http buffer timeount (default 10 Sekunden)</li> + <li><b>maxListItems</b> – Definiert die max. Anzahl der anzuzeigenden Playerlisteinträge (default 100)</li> + <li><b>playerBrowsingTimeout</b> – Definiert den Inaktivitäts-Timeout beim Browsen der Playerlist. Nach diesem Timeout fällt das Modul in den "home"-State zurück. Die Playerreadings werden wieder aktualisiert (default 180 Sekunden)</li> + <li><b>readingFnAttributes</b></li> + <li><b>requestTimeout</b> – Optionalles Attribut für http responses (default 4 Sekunden)</li> </ul> - </ul> - <b>Readings</b><br><br> - <ul> - <ul> - <li><b>albumArt</b> - Link zum aktuellen Album art oder Radiostation.</li> - <li><b>elapseTime</b> - Aktuelle Zeit des abgespielten Audiostückes.</li> - <li><b>inetRadioFavorite[Wert]</b> - Name des/der gespeicherten Favoriten.</li> - <li><b>inetRadioPreset[Wert]</b> - Name des/der gespeicherten Presets.</li> - <li><b>mute</b> - Abfrage des Stummschaltungstatus (on|off).</li> - <li><b>playing</b> - Abfrage des aktuelle Playierstatus (yes|no).</li> - <li><b>power</b> - Abfrage des aktuellen Gerätezustands (on|absent).</li> - <li><b>presence</b> - Abfrage der Geräteanwesenheit (present|absent).</li> - <li><b>state</b> - Abfrage des aktuellen 'state'-Status (on|absent).</li> - <li><b>subtitle</b> - Untertiltel des abgespielten Audiostückes.</li> - <li><b>title</b> - Titel des abgespielten Audiostückes.</li> - <li><b>totalInetRadioFavorites</b> - Anzahl der gespeicherten Favoriten.</li> - <li><b>totalInetRadioPresets</b> - Anzahl der gespeicherten Presets.</li> - <li><b>totalTime</b> Gesamtspieldauer des Audiostückes.</li> - <li><b>volume</b> - Aktuelle relative Lautstärke (0..100).</li> - <li><b>volumeStraight</b> - Aktuelle absolute Lautstärke (0..64).</li> - </ul> - </ul><br> - <b>Bemerkung des Entwicklers</b><br><br> - <ul> - Trivial: Um das Gerät fernbedienen zu können, muss es an das Ethernet-Netzwerk angeschlossen und erreichbar sein.<br> - Es gibt keine Möglichkeit, den Zustand Power-on/Standby des Gerätes abzufragen. Diese Limitierung liegt auf Seiten des Gerätes.<br> - </ul><br> + </ul> </ul> + =end html_DE -=cut \ No newline at end of file + +=cut