############################################################################## # # $Id$ # # 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). # It seems the 3000 family e.g. NP3500, NP3700, NP3900 use that port. # # Copyright by Radoslaw Watroba # (e-mail: ra666ack at googlemail dot com) # # This file is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # ############################################################################## package main; use strict; use warnings; use Time::HiRes qw(gettimeofday sleep); use Time::Piece; use POSIX qw{strftime}; use HttpUtils; ################################### sub PHILIPS_AUDIO_Initialize { my ($hash) = @_; $hash->{DefFn} = "PHILIPS_AUDIO_Define"; $hash->{GetFn} = "PHILIPS_AUDIO_Get"; $hash->{SetFn} = "PHILIPS_AUDIO_Set"; $hash->{AttrFn} = "PHILIPS_AUDIO_Attr"; $hash->{UndefFn} = "PHILIPS_AUDIO_Undefine"; $hash->{AttrList} = "do_not_notify:0,1 disable:0,1 request-timeout:1,2,3,4,5 ".$readingFnAttributes; return; } ################################### sub PHILIPS_AUDIO_GetStatus { my ($hash, $local) = @_; my $name = $hash->{NAME}; my $power; $local = 0 unless(defined($local)); return "" if((!defined($hash->{IP_ADDRESS})) or (!defined($hash->{helper}{OFF_INTERVAL})) or (!defined($hash->{helper}{ON_INTERVAL}))); my $device = $hash->{IP_ADDRESS}; PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); PHILIPS_AUDIO_ResetTimer($hash) unless($local == 1); return; } ################################### sub PHILIPS_AUDIO_Get { my ($hash, @a) = @_; my $what; my $return; my $name = $hash->{NAME}; my $address = $hash->{IP_ADDRESS}; $hash->{IP_ADDRESS} = $address; return "argument is missing" if(int(@a) != 2); if(not defined($hash->{MODEL})) { return "Please provide the model information as argument."; } $what = $a[1]; if(exists($hash->{READINGS}{$what})) { if(defined($hash->{READINGS}{$what})) { return $hash->{READINGS}{$what}{VAL}; } else { return "no such reading: $what"; } } else { $return = "unknown argument $what, choose one of"; foreach my $reading (keys %{$hash->{READINGS}}) { $return .= " $reading:noArg"; } return $return; } } ################################### sub PHILIPS_AUDIO_Set { my ($hash, @a) = @_; my $name = $hash->{NAME}; my $port = $hash->{PORT}; if(not defined($hash->{MODEL})) { return "Please provide the model information as argument."; } return "No Argument given" if(!defined($a[1])); my $what = $a[1]; my $usage; $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 ". #"input:aux,internetRadio,mediaLibrary,onlineServices ". "inetRadioPreset:1,2,3,4,5,6,7,8,9,10 ". "inetRadioFavorite:1,2,3,4,5,6,7,8,9,10 ". "statusRequest:noArg ". #"addToFavourites:noArg ". #"removeFromFavourites:noArg ". "repeat:single,all,off ". #"home:noArg ". "mute:noArg "; Log3 $name, 5, "PHILIPS_AUDIO ($name) - set ".join(" ", @a); if($what eq "standbyButton") { PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$STANDBY", "",$what, "noArg"); } elsif($what eq "aux") { PHILIPS_AUDIO_SendCommand($hash, "/aux", "",$what, $a[2]); } 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]); } elsif($what eq "shuffle") { if($a[2] eq "on") { PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_ON", "",$what, $a[2]); } elsif($a[2] eq "off") { PHILIPS_AUDIO_SendCommand($hash, "/MODE\$SHUFFLE_OFF", "",$what, $a[2]); } else { return $usage; } } elsif($what eq "repeat") { if($a[2] eq "single") { PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_SINGLE", "",$what, $a[2]); } elsif($a[2] eq "all") { PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_ALL", "",$what, $a[2]); } elsif($a[2] eq "off") { PHILIPS_AUDIO_SendCommand($hash, "/MODE\$REPEAT_OFF", "",$what, $a[2]); } else { return $usage; } } elsif($what eq "statusRequest") { PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "","nowplay", "noArg"); } elsif($what eq "addToFavourites") { PHILIPS_AUDIO_SendCommand($hash, "/CTRL\$ADD2FAV", "",$what, "noArg"); } elsif($what eq "removeFromFavourites") { 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 "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 "volumeStraight") { if($a[2] >= 0 and $a[2] <= 64) { $hash->{helper}{targetVolume} = $a[2]; PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]); } else { return "volumeStraight must be in the range 0...64."; } } elsif($what eq "volume") { if($a[2] >= 0 and $a[2] <= 100) { $hash->{helper}{targetVolume} = PHILIPS_AUDIO_volume_rel2abs($hash, $a[2]); PHILIPS_AUDIO_SendCommand($hash, "/VOLUME\$VAL\$".$a[2], "",$what, $a[2]); } else { return "volumeStraight must be in the range 0...100."; } } elsif($what eq "nowplay") { PHILIPS_AUDIO_SendCommand($hash, "/nowplay", "",$what, "noArg"); } else { return $usage; } return; } ############################# sub PHILIPS_AUDIO_Define { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; if(! @a >= 4) { my $msg = "Wrong syntax: define PHILIPS_AUDIO [] [] "; Log3 $name, 2, $msg; return $msg; } if(defined($a[2])) { $hash->{MODEL} = $a[2]; } $hash->{IP_ADDRESS} = $a[3]; $hash->{PORT} = 8889; # if an update interval was given which is greater than zero, use it. if(defined($a[4]) and $a[4] > 0) { $hash->{helper}{OFF_INTERVAL} = $a[4]; # Minimum interval 3 sec if($hash->{helper}{OFF_INTERVAL} < 3) { $hash->{helper}{OFF_INTERVAL} = 3; } } else { $hash->{helper}{OFF_INTERVAL} = 30; } if(defined($a[5]) and $a[5] > 0) { $hash->{helper}{ON_INTERVAL} = $a[5]; # Minimum interval 3 sec if($hash->{helper}{ON_INTERVAL} < 3) { $hash->{helper}{ON_INTERVAL} = 3; } } else { $hash->{helper}{ON_INTERVAL} = $hash->{helper}{OFF_INTERVAL}; } unless(exists($hash->{helper}{AVAILABLE}) and ($hash->{helper}{AVAILABLE} == 0)) { $hash->{helper}{AVAILABLE} = 1; readingsSingleUpdate($hash, "presence", "present", 1); } # start the status update timer $hash->{helper}{DISABLED} = 0 unless(exists($hash->{helper}{DISABLED})); PHILIPS_AUDIO_ResetTimer($hash,0); return; } ########################## sub PHILIPS_AUDIO_Attr { my @a = @_; my $hash = $defs{$a[1]}; if($a[0] eq "set" && $a[2] eq "disable") { if($a[3] eq "0") { $hash->{helper}{DISABLED} = 0; PHILIPS_AUDIO_GetStatus($hash, 1); } elsif($a[3] eq "1") { $hash->{helper}{DISABLED} = 1; } } elsif($a[0] eq "del" && $a[2] eq "disable") { $hash->{helper}{DISABLED} = 0; PHILIPS_AUDIO_GetStatus($hash, 1); } # 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); return; } ############################################################################################################ # # Begin of helper functions # ############################################################################################################ ############################# # sends a command to the receiver via HTTP sub PHILIPS_AUDIO_SendCommand { 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"; 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 }); 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}; 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"; 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); $hash->{STATE} = "absent"; } $hash->{helper}{AVAILABLE} = 0; } elsif($data ne "") { Log3 $name, 5, "PHILIPS_AUDIO ($name) - got response for \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $data"; 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); } $hash->{helper}{AVAILABLE} = 1; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "power", "on"); readingsBulkUpdate($hash, "state","on"); $hash->{STATE} = "on"; if($cmd eq "standbyButton") { if($data =~ /SUCCESS/) { #readingsBulkUpdate($hash, "power", "on"); #readingsBulkUpdate($hash, "state","on"); } } elsif($cmd eq "mute") { if($data =~ /SUCCESS/) { readingsBulkUpdate($hash, "mute", "on"); } } elsif($cmd eq "unmute") { if($data =~ /SUCCESS/) { readingsBulkUpdate($hash, "mute", "off"); } } elsif($cmd eq "removeFromFavourites") { # evtl. for future use } elsif($cmd eq "addToFavourites") { # evtl. for future use } elsif($cmd eq "inetRadioPreset") { # 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", "", "inetRadioPreset", $hash->{helper}{inetRadioPreset}); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; # Presets PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$001\$0", "","inetRadioPreset", $hash->{helper}{inetRadioPreset}); } elsif($hash->{helper}{cmdStep} == 3) { $hash->{helper}{cmdStep} = 4; # Preset select PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioPreset})."\$1", "","inetRadioPreset", $hash->{helper}{inetRadioPreset}); } } 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}); } elsif($hash->{helper}{cmdStep} == 2) { $hash->{helper}{cmdStep} = 3; # Favorite Presets PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$02\$002\$0", "","inetRadioFavorite", $hash->{helper}{inetRadioFavorite}); } elsif($hash->{helper}{cmdStep} == 3) { $hash->{helper}{cmdStep} = 4; # Favorite Preset select PHILIPS_AUDIO_SendCommand($hash, "/nav\$03\$03\$".sprintf("%03d", $hash->{helper}{inetRadioFavorite})."\$1", "","inetRadioFavorite", $hash->{helper}{inetRadioFavorite}); } } elsif($cmd eq "play_pause") { if($data =~ /SUCCESS/) { #readingsBulkUpdate($hash, "play_pause", "on"); #readingsBulkUpdate($hash, "stop", "off"); } } elsif($cmd eq "stop") { if($data =~ /STOP/) { readingsBulkUpdate($hash, "playing", "no"); } } elsif($cmd eq "volume" or $cmd eq "volumeStraight") { if($data =~ /SUCCESS/) { readingsBulkUpdate($hash, "volumeStraight", $hash->{helper}{targetVolume}); my $targetVolume = $hash->{helper}{targetVolume}; readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $targetVolume)); } } elsif($cmd eq "nowplay") { if($data =~ /'title':'\\'(.+)\\''/) { readingsBulkUpdate($hash, "title", PHILIPS_AUDIO_STREAMIUMNP2txt($1)); } else { readingsBulkUpdate($hash, "title", ""); } if($data =~ /'title':'(.+)'/) { readingsBulkUpdate($hash, "title", PHILIPS_AUDIO_STREAMIUMNP2txt($1)); } else { readingsBulkUpdate($hash, "title", ""); } if($data =~ /'subTitle':'(.+)'/) { readingsBulkUpdate($hash, "subtitle", $1); } else { readingsBulkUpdate($hash, "subtitle", ""); } if($data =~ /'albumArt':'(.+)'/) { readingsBulkUpdate($hash, "albumArt", $1); } else { readingsBulkUpdate($hash, "albumArt", ""); } if($data =~ /'volume':(.+),/) { readingsBulkUpdate($hash, "volumeStraight", $1); readingsBulkUpdate($hash, "volume", PHILIPS_AUDIO_volume_abs2rel($hash, $1)); } else { readingsBulkUpdate($hash, "volumeStraight", "0"); readingsBulkUpdate($hash, "volume", "0"); } if($data =~ /'elapsetime':(.+),/) { readingsBulkUpdate($hash, "elapseTime", strftime("\%H:\%M:\%S", gmtime($1))); } else { readingsBulkUpdate($hash, "elapseTime", ""); } if($data =~ /'totaltime':(.+),/) { # Playing radio delivers that total time if($1 eq "65535") { readingsBulkUpdate($hash, "totalTime", "infinite"); } else { readingsBulkUpdate($hash, "totalTime", strftime("\%H:\%M:\%S", gmtime($1))); } } else { readingsBulkUpdate($hash, "totalTime", ""); } if($data =~ /'muteStatus':(.+),/) { if($1 == 1) { readingsBulkUpdate($hash, "mute", "on"); } else { readingsBulkUpdate($hash, "mute", "off"); } } # typo in the (buggy) Streamium firmware... if($data =~ /'playStaus':(.+),/) { if($1 == 1) { readingsBulkUpdate($hash, "playing", "yes"); } else { readingsBulkUpdate($hash, "playing", "no"); } } else { readingsBulkUpdate($hash, "playing", "no"); } } # Eventual future UPNP implementation. Requests IO::Socket::Multicast non-standard module. elsif ($cmd eq "getModel") { if($data =~ /(.+)<\/friendlyName>/) { $hash->{FRIENDLY_NAME} = $1; } if($data =~ /(.+)<\/UDN>/) { $hash->{UNIQUE_DEVICE_NAME} = uc($1); } my $modelName = ""; my $modelNumber = ""; if($data =~ /(.+)<\/modelName>/) { $modelName = $1; } if($data =~ /(.+)<\/modelNumber>/) { $modelNumber = $1; } # Combine both strings if(($modelName ne "") and ($modelNumber ne "")) { $hash->{MODEL} = $modelName . $modelNumber; } if($data =~ /(.+)<\/serialNumber>/) { $hash->{SERIAL_NUMBER} = uc($1); } if($data =~ /(.+)<\/modelDescription>/) { $hash->{MODEL_DESCRIPTION} = $1; } # Replace \n, \r, \t from the string for XML parsing # replace \n by "" $data =~ s/\n//g; # replace \t by "" $data =~ s/\t//g; # replace \r by "" $data =~ s/\r//g; if($data =~ /(.+?)<\/iconList>/) { my $address = $hash->{IP_ADDRESS}; my $i = 1; while ($data =~ /(.+?)<\/url>/g) { $hash->{"NP_ICON_$i"} = "http://".$address.":".$arg."/".$1; $i++; } } } readingsEndUpdate($hash, 1); } return; } ############################# # converts straight volume in percentage volume (volumestraightmin .. volumestraightmax => 0 .. 100%) sub PHILIPS_AUDIO_volume_rel2abs { my ($hash, $percentage) = @_; return int(($percentage * 64 / 100 )); } ############################# # converts relative volume to "straight" volume (0 .. 100% => volumestraightmin .. volumestraightmax) sub PHILIPS_AUDIO_volume_abs2rel { my ($hash, $absolute) = @_; return int(int($absolute * 100) / int(64)); } ############################# # Restarts the internal status request timer according to the given interval or current receiver state sub PHILIPS_AUDIO_ResetTimer { my ($hash, $interval) = @_; RemoveInternalTimer($hash); if($hash->{helper}{DISABLED} == 0) { if(defined($interval)) { InternalTimer(gettimeofday()+$interval, "PHILIPS_AUDIO_GetStatus", $hash, 0); } 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); } else { InternalTimer(gettimeofday() + $hash->{helper}{OFF_INTERVAL}, "PHILIPS_AUDIO_GetStatus", $hash, 0); } } return; } ############################# # convert all HTML entities into UTF-8 equivalent sub PHILIPS_AUDIO_STREAMIUMNP2txt { my ($string) = @_; $string =~ s/\\'//g; return $string; } 1; =pod =begin html

PHILIPS_AUDIO

    Define

      define <name> PHILIPS_AUDIO <device model> <ip-address> [<status_interval>]

      define <name> PHILIPS_AUDIO <device model> [<off_status_interval>] [<on_status_interval>]


      This module controls a Philips Audio Player e.g. MCi, Streamium or Fidelio and (potentially) any other device including a navigation server.
      To check, open the following URL in the browser: http://[ip # of your device]:8889/index

      Currently implemented features:

      • Power on/off
      • Internet Radio Preset Selection
      • Input selection
      • Volume +/-
      • Mute on/off
      • ...

      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.
      <off_status_interval> is a parameter used in case the device is powered off or not available.
      <on_status_interval> is a parameter used in case the device is powered on.
      If both parameters are unset, a default value 30 (seconds) for both is used.
      If <off_status_interval> is set only the same value is used for both parameters.
      Due to a relatively low-performance of the devices the minimum interval is set to 3 seconds.
      The internal task periodically reads the status of the Network Player (power state, volume and mute status etc.) and triggers notify/filelog commands.

      Example:


        Add the following code into the fhem.cfg configuration file and restart fhem:

        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset

        # With custom status interval of 60 seconds
        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 60
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset

        # With custom "off"-interval of 60 seconds and "on"-interval of 10 seconds
        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 60 10
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset



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

      Currently, the following commands are defined.
      Note: Commands and parameters are case sensitive.


        Available commands:

      • aux  -   Switches to the AUX input (MP3 Link or similar).
      • inetRadioPreset [1..10]   -   Selects an internet radio preset (May take some seconds...).
      • inetRadioFavorite [1..10]   -   Selects an internet radio favorite (May take some seconds...).
      • mute  -   Mutes the device.
      • unmute  -   Unmutes the device.
      • next   -   Selects the next song, preset etc.
      • play_pause   -   Toggles PLAY/PAUSE.
      • previous   -   Selects the previous song, preset etc.
      • repeat [single|all|off]   -   Sets the repeat mode.
      • shuffle [on|off]   -   Sets the shuffle mode.
      • standbyButton   -   Toggles between standby and power on.
      • statusRequest   -   Updates the readings.
      • stop   -   Stops the player.
      • volume   -   Sets the relative volume 0...100%.
      • volumeStraight   -   Sets the absolute volume 0...64.


      A typical example is powering the device remotely and tuning the favourite radio station:

      Add the following code into the fhem.cfg configuration file:


        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 30 5
        attr PHAUDIO_player webCmd volume:mute:inetRadioPreset


      Add the following code into the 99_MyUtils.pm file:

        sub startMyFavouriteRadioStation()
        {
          fhem "set PHAUDIO_player inetRadioPreset 1";
          sleep 1;
          fhem "set PHAUDIO_player volume 30";
        }


      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...

      Now the function can be called by typing the following line in the FHEM command line or by the notify-definitions:

        {startMyFavouriteRadioStation()}

    Get
      get <name> <reading>

      Currently, the 'get' command returns reading values only. For a specific list of possible values, see section "Generated Readings".

    Attributes

      • do_not_notify
      • readingFnAttributes
      • request-timeout

      • Optional attribute change the response timeout in seconds for all queries to the receiver.
        Possible values: 1...5 seconds. Default value is 4 seconds.

      • disable

      • Optional attribute to disable the internal cyclic status update of the receiver. Manual status updates via statusRequest command is still possible.
        Possible values: 0 → perform cyclic status update, 1 → don't perform cyclic status updates.


    Readings

      • albumArt - Link to current album art or radio station.
      • elapseTime - Elapse time of the played audio.
      • mute - Reports the mute status (on|off).
      • playing - Reports the current playier status (yes|no).
      • power - Reports the current power status (on|absent).
      • presence - Reports the current presence (present|absent).
      • state - Reports the current state status (on|absent).
      • subtitle - Reports the current subtitle of played audio.
      • title - Reports the current title of played audio.
      • totalTime - Reports the total time of the played audio.
      • volume - Reports current relative volume (0..100).
      • volumeStraight - Reports current absolute volume (0..64).

    Implementer's note

      Trivial: In order to use that module the network player must be connected to the Ethernet.
      There's no possibility to read back the current power on/standby status from the device. This fuctionality is missing in the server application.

=end html =begin html_DE

PHILIPS_AUDIO

    Define

      define <name> PHILIPS_AUDIO <device model> <ip-address> [<status_interval>]

      define <name> PHILIPS_AUDIO <device model> [<off_status_interval>] [<on_status_interval>]


      Mit Hilfe dieses Moduls lassen sich Philips Audio Netzwerk Player wie z.B. MCi, Streamium oder Fidelio via Ethernet steuern.
      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.

      Die aktuelle Implementierung ermöglicht u.a. den folgenden Funktionsumfang:

      • Power on/off
      • Internet Radio Preset Auswahl
      • Input Auswahl
      • Volume +/-
      • Mute on/off
      • ...

      Eine PHILIPS_AUDIO Definition initiiert einen internen Task, der von FHEM zyklisch abgearbeitet wird.
      Das Intervall (in Sekunden) kann für die Zustände <on_status_interval> und <off_status_interval> optional gesetzt werden.
      <off_status_interval> steht für das Intervall, wenn das Gerät ausgeschaltet/abwesend ist.
      <on_status_interval> steht für das Intervall, wenn das Gerät eingeschaltet/verfügbar ist.
      Wenn keine Parameter angegeben wurden, wird ein Default-Wert von 30 Sekunden für beide gesetzt.
      Wenn nur <off_status_interval> gesetzt wird, gilt dieser Wert für beide Zustände (eingeschaltet/ausgeschaltet).
      Der Task liest zyklisch grundlegende Parameter vom Player und triggert notify/filelog Befehle.
      Aufgrund der recht schwachen Rechenleistung der Player wurde das minimale Intervall auf 3 Sekunden beschränkt.

      Beispiel:


        Definition in der fhem.cfg Konfigurationsdatei:

        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset

        # 60 Sekunden Intervall
        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 60
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset

        # 60 Sekunden Intervall für "off" und 10 Sekunden für "on"
        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 60 10
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset


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

      Aktuell sind folgende Befehle implementiert:
      Bemerkung: Bitte bei den Befehlen und Parametern die Groß- und Kleinschreibung beachten.


      • aux  -   Schaltet auf den AUX Eingang um (MP3 Link oder ähnlich.).
      • inetRadioPreset [1..10]   -   Wählt die Internetradio Voreinstellung (Das Umschalten kann einige Sekunden dauern...).
      • inetRadioFavorite [1..10]   -   Wählt den Internetradio-Lieblingssender (Das Umschalten kann einige Sekunden dauern...).
      • mute  -   Stummschaltung des Players.
      • unmute  -   Deaktivierung der Stummschaltung.
      • next   -   Wählt den nächten Titel, Voreinstellung etc.
      • play_pause   -   Schaltet um zwischen PLAY um PAUSE.
      • previous   -   Wählt den vorherigen Titel, Voreinstellung etc.
      • repeat [single|all|off]   -   Bestimmt den Wiederholungsmodus.
      • shuffle [on|off]   -   Bestimmt den Zufallswiedergabemodus.
      • standbyButton   -   Schaltet um zwischen Standby und Power on.
      • statusRequest   -   Readings Update.
      • stop   -   Stoppt den Player.
      • volume   -   Setzt die relative Lautstärke 0...100%.
      • volumeStraight   -   Setzt die absolute Lautstärke 0...64.


      Ein typisches Beispiel ist das Einschalten des Gerätes und das Umschalten auf den Lieblingsradiosender:

      Beispieldefinition in der fhem.cfg Konfigurationsdatei:


        define PHAUDIO_player PHILIPS_AUDIO NP3900 192.168.0.15 30 5
        attr PHAUDIO_player webCmd input:volume:mute:inetRadioPreset


      Folgender Code kann anschließend in die Datei 99_MyUtils.pm eingebunden werden:

        sub startMyFavouriteRadioStation()
        {
          fhem "set PHAUDIO_player inetRadioPreset 1";
          sleep 1;
          fhem "set PHAUDIO_player volume 30";
        }


      Bemerkung: Aufgrund der relativ langsamen Befehlsverarbeitung im Player im Vergleich zur asynchronen Ethernet-Kommunikation, kann es vorkommen, dass veraltete Statusinformationen zurückgesendet werden.
      Aus diesem Grund wird empfohlen, während der Automatisierung zwischen den 'set' und 'get' Befehlen ein Delay einzubauen.


      Die Funktion kann jetzt in der FHEM Befehlszeile eingegeben oder in die Notify-Definitionen eingebunden werden.

        {startMyFavouriteRadioStation()}

    Get get <name> <reading>

    Aktuell liefert der Befehl 'get' ausschließlich Reading-Werte (s. Abschnitt "Readings").

    Attribute

      • do_not_notify
      • readingFnAttributes

      • request-timeout

      • Optionales Attribut, um das HTTP response timeout zu beeinflußen.
        Mögliche Werte: 1...5 Sekunden. Default Wert ist 4 Sekunden.

      • disable

      • Optionales Attribut zum Deaktivieren des internen zyklischen Timers zum Aktualisieren des NP-Status. Manuelles Update ist nach wie vor möglich.
        Mögliche Werte: 0 → Zyklisches Update aktiv., 1 → Zyklisches Update inaktiv.


    Readings

      • albumArt - Link zum aktuellen Album art oder Radiostation.
      • elapseTime - Aktuelle Zeit des abgespielten Audiostückes.
      • mute - Abfrage des Stummschaltungstatus (on|off).
      • playing - Abfrage des aktuelle Playierstatus (yes|no).
      • power - Abfrage des aktuellen Gerätezustands (on|absent).
      • presence - Abfrage der Geräteanwesenheit (present|absent).
      • state - Abfrage des aktuellen 'state'-Status (on|absent).
      • subtitle - Untertiltel des abgespielten Audiostückes.
      • title - Titel des abgespielten Audiostückes.
      • totalTime Gesamtspieldauer des Audiostückes.
      • volume - Aktuelle relative Lautstärke (0..100).
      • volumeStraight - Aktuelle absolute Lautstärke (0..64).

    Bemerkung des Entwicklers

      Trivial: Um das Gerät fernbedienen zu können, muss es an das Ethernet-Netzwerk angeschlossen und erreichbar sein.
      Es gibt keine Möglichkeit, den Zustand Power-on/Standby des Gerätes abzufragen. Diese Limitierung liegt auf Seiten des Gerätes.

=end html_DE =cut