2018-04-22 19:53:00 +00:00
# ############################################################################
2016-10-01 16:43:31 +00:00
# $Id$
2014-07-06 22:13:50 +00:00
#
2015-03-19 20:32:45 +00:00
# FHEM Module for Squeezebox Servers
2014-07-06 22:13:50 +00:00
#
# ############################################################################
#
# used to interact with Squeezebox server
#
# ############################################################################
#
2015-05-08 20:52:02 +00:00
# Written by bugster_de
#
2017-05-28 19:22:04 +00:00
# Contributions from: Siggi85, Oliv06, ChrisD, Eberhard
2015-05-08 20:52:02 +00:00
#
# ############################################################################
#
2014-07-06 22:13:50 +00:00
# This is absolutley open source. Please feel free to use just as you
2017-05-28 19:22:04 +00:00
# like. Please note, that no warranty is given and no liability
2014-07-06 22:13:50 +00:00
# granted
#
# ############################################################################
#
# we have the following readings
# power on|off
# version the version of the SB Server
2017-05-28 19:22:04 +00:00
# serversecure is the CLI port protected with a password?
2014-07-06 22:13:50 +00:00
#
# ############################################################################
#
# we have the following attributes
# alivetimer time frequency to set alive signals
# maxfavorites maximum number of favorites we handle at FHEM
#
# ############################################################################
# we have the following internals (all UPPERCASE)
# IP the IP of the server
# CLIPORT the port for the CLI interface of the server
#
# ############################################################################
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
package main ;
use strict ;
use warnings ;
use IO::Socket ;
use URI::Escape ;
2016-10-01 16:43:31 +00:00
# include for using the perl ping command
2014-07-06 22:13:50 +00:00
use Net::Ping ;
2018-04-22 19:53:00 +00:00
use Encode qw( decode encode ) ; # CD 0009 hinzugefügt
2017-05-28 19:22:04 +00:00
#use Text::Unidecode;
2014-07-06 22:13:50 +00:00
2016-10-01 16:43:31 +00:00
no if $] >= 5.017011 , warnings = > 'experimental::smartmatch' ;
2014-07-06 22:13:50 +00:00
# this will hold the hash of hashes for all instances of SB_SERVER
my % favorites ;
my $ favsetstring = "favorites: " ;
# this is the buffer for commands, we queue up when server is power=off
my % SB_SERVER_CmdStack ;
2017-05-28 19:22:04 +00:00
my @ SB_SERVER_AL_PLS ;
my @ SB_SERVER_FAVS ;
my @ SB_SERVER_PLS ;
my @ SB_SERVER_SM ;
2014-07-06 22:13:50 +00:00
# include this for the self-calling timer we use later on
2016-10-01 16:43:31 +00:00
use Time::HiRes qw( gettimeofday time ) ;
2014-07-06 22:13:50 +00:00
use constant { true = > 1 , false = > 0 } ;
use constant { TRUE = > 1 , FALSE = > 0 } ;
2018-04-22 19:53:00 +00:00
use constant SB_SERVER_VERSION = > '0049' ;
2017-05-28 19:22:04 +00:00
my $ SB_SERVER_hasDataDumper = 1 ; # CD 0024
2014-07-06 22:13:50 +00:00
2017-06-04 10:03:33 +00:00
sub SB_SERVER_RemoveInternalTimers ($) ;
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# Initialisation routine called upon start-up of FHEM
# ----------------------------------------------------------------------------
sub SB_SERVER_Initialize ( $ ) {
my ( $ hash ) = @ _ ;
require "$attr{global}{modpath}/FHEM/DevIo.pm" ;
# Provider
$ hash - > { ReadFn } = "SB_SERVER_Read" ;
$ hash - > { WriteFn } = "SB_SERVER_Write" ;
$ hash - > { ReadyFn } = "SB_SERVER_Ready" ;
$ hash - > { Clients } = ":SB_PLAYER:" ;
my % matchList = (
"1:SB_PLAYER" = > "^SB_PLAYER:" ,
) ;
$ hash - > { MatchList } = \ % matchList ;
# Normal devices
$ hash - > { DefFn } = "SB_SERVER_Define" ;
$ hash - > { UndefFn } = "SB_SERVER_Undef" ;
$ hash - > { ShutdownFn } = "SB_SERVER_Shutdown" ;
$ hash - > { GetFn } = "SB_SERVER_Get" ;
$ hash - > { SetFn } = "SB_SERVER_Set" ;
$ hash - > { AttrFn } = "SB_SERVER_Attr" ;
2014-07-23 09:01:39 +00:00
$ hash - > { NotifyFn } = "SB_SERVER_Notify" ;
2014-07-06 22:13:50 +00:00
$ hash - > { AttrList } = "alivetimer maxfavorites " ;
$ hash - > { AttrList } . = "doalivecheck:true,false " ;
$ hash - > { AttrList } . = "maxcmdstack " ;
2015-03-19 20:32:45 +00:00
$ hash - > { AttrList } . = "httpport " ;
2018-04-22 19:53:00 +00:00
$ hash - > { AttrList } . = "disable:0,1 " ; # CD 0046
2017-05-28 19:22:04 +00:00
$ hash - > { AttrList } . = "enablePlugins " ;
2018-04-22 19:53:00 +00:00
$ hash - > { AttrList } . = "ignoredIPs ignoredMACs internalPingProtocol:icmp,tcp,udp,syn,stream,none " ; # CD 0021 none hinzugefügt
2014-07-06 22:13:50 +00:00
$ hash - > { AttrList } . = $ readingFnAttributes ;
2017-05-28 19:22:04 +00:00
# CD 0024
eval "use Data::Dumper" ;
$ SB_SERVER_hasDataDumper = 0 if ( $@ ) ;
}
# CD 0032 start
sub SB_SERVER_SetAttrList ( $ ) {
my ( $ hash ) = @ _ ;
my $ attrList ;
$ attrList = "alivetimer maxfavorites " ;
$ attrList . = "doalivecheck:true,false " ;
$ attrList . = "maxcmdstack " ;
$ attrList . = "httpport " ;
2018-04-22 19:53:00 +00:00
$ attrList . = "disable:0,1 " ; # CD 0046
$ attrList . = "ignoredIPs ignoredMACs internalPingProtocol:icmp,tcp,udp,syn,stream,none " ; # CD 0021 none hinzugefügt
2017-05-28 19:22:04 +00:00
my $ applist = "enablePlugins" ;
if ( defined ( $ hash - > { helper } { apps } ) ) {
$ applist . = ":multiple-strict" ;
foreach my $ app ( sort keys % { $ hash - > { helper } { apps } } ) {
$ applist . = ",$app" ;
}
}
$ attrList . = "$applist " ;
$ attrList . = $ readingFnAttributes ;
$ modules { $ defs { $ hash - > { NAME } } { TYPE } } { AttrList } = $ attrList ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0032 end
2014-07-06 22:13:50 +00:00
2018-04-22 19:53:00 +00:00
# CD 0046 start
# ----------------------------------------------------------------------------
# connect to server
# ----------------------------------------------------------------------------
sub SB_SERVER_TryConnect ( $$ ) {
my ( $ hash , $ reopen ) = @ _ ; # CD 0047 reopen hinzugefügt
return if ( $ hash - > { CLICONNECTION } eq 'on' ) ;
return if ( IsDisabled ( $ hash - > { NAME } ) ) ;
delete $ hash - > { helper } { disableReconnect } if ( defined ( $ hash - > { helper } { disableReconnect } ) ) ;
if ( SB_SERVER_IsValidIPV4 ( $ hash - > { IP } ) ) {
return DevIo_OpenDev ( $ hash , $ reopen , "SB_SERVER_DoInit" ) ;
} else {
return DevIo_OpenDev ( $ hash , $ reopen , "SB_SERVER_DoInit" , \ & SB_SERVER_DevIoCallback )
}
}
sub SB_SERVER_DevIoCallback ($$)
{
my ( $ hash , $ err ) = @ _ ;
my $ name = $ hash - > { NAME } ;
if ( $ err )
{
Log3 $ name , 2 , "SB_SERVER_DevIoCallback ($name) - unable to connect: $err" ;
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
DevIo_Disconnected ( $ hash ) ; # CD 0048 wird nicht von DevIo gemacht ?
SB_SERVER_setStates ( $ hash , "disconnected" ) ; # CD 0048 wird nicht von DevIo gemacht ?
}
}
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# called when defining a module
# ----------------------------------------------------------------------------
sub SB_SERVER_Define ( $$ ) {
my ( $ hash , $ def ) = @ _ ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
#my $name = $hash->{NAME};
Log3 ( $ hash , 4 , "SB_SERVER_Define: called" ) ;
# first of all close existing connections
DevIo_CloseDev ( $ hash ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my @ a = split ( "[ \t][ \t]*" , $ def ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# do we have the right number of arguments?
if ( ( @ a < 3 ) || ( @ a > 7 ) ) {
Log3 ( $ hash , 3 , "SB_SERVER_Define: falsche Anzahl an Argumenten" ) ;
return ( "wrong syntax: define <name> SB_SERVER <serverip[:cliport]>" .
2015-03-19 20:32:45 +00:00
"[USER:username] [PASSWORD:password] " . # CD 0007 changed PASSWord to PASSWORD
"[RCC:RCC_Name] [WOL:WOLName] [PRESENCE:PRESENCEName]" ) ; # CD 0007 added PRESENCE
2014-07-06 22:13:50 +00:00
}
# remove the name and our type
my $ name = shift ( @ a ) ;
shift ( @ a ) ;
# assign safe default values
$ hash - > { IP } = "127.0.0.1" ;
$ hash - > { CLIPORT } = 9090 ;
$ hash - > { WOLNAME } = "none" ;
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { wolSetCmd } = ' ' ; # CD 0047
$ hash - > { helper } { wolSetValue } = 'on' ; # CD 0047
2015-03-19 20:32:45 +00:00
$ hash - > { PRESENCENAME } = "none" ; # CD 0007
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { presenceReading } = 'state' ; # CD 0047
$ hash - > { helper } { presenceValuePresent } = 'present' ; # CD 0047
$ hash - > { helper } { presenceValueAbsent } = 'absent' ; # CD 0047
2014-07-06 22:13:50 +00:00
$ hash - > { RCCNAME } = "none" ;
$ hash - > { USERNAME } = "?" ;
$ hash - > { PASSWORD } = "?" ;
2017-05-28 19:22:04 +00:00
2018-04-22 19:53:00 +00:00
# CD 0048 versuchen Namen/Adresse zu säubern
$ a [ 0 ] =~ s/^https:\/\/// ;
$ a [ 0 ] =~ s/^http:\/\/// ;
$ a [ 0 ] =~ s/\/$// ;
# CD 0046 Hostnamen statt IP-Adresse zulassen
$ hash - > { DeviceName } = $ a [ 0 ] ;
if ( $ a [ 0 ] =~ m/^(.+):([0-9]+)$/ ) {
$ hash - > { IP } = $ 1 ;
$ hash - > { CLIPORT } = $ 2 ;
} else {
$ hash - > { IP } = $ a [ 0 ] ;
}
2017-05-28 19:22:04 +00:00
my ( $ user , $ password ) ;
my @ newDef ;
2018-04-22 19:53:00 +00:00
2017-07-26 19:21:33 +00:00
# CD 0041 start
my @ notifyregexp ;
push @ notifyregexp , "global" ;
push @ notifyregexp , $ hash - > { NAME } ;
# CD 0041 end
2014-07-06 22:13:50 +00:00
# parse the user spec
foreach ( @ a ) {
if ( $ _ =~ /^(RCC:)(.*)/ ) {
$ hash - > { RCCNAME } = $ 2 ;
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2017-07-26 19:21:33 +00:00
push @ notifyregexp , $ 2 ; # CD 0041
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(WOL:)(.*)/ ) {
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2018-04-22 19:53:00 +00:00
my @ pp = split ':' , $ 2 ; # CD 0047
$ hash - > { WOLNAME } = $ pp [ 0 ] ;
$ hash - > { helper } { wolSetCmd } = $ pp [ 1 ] if defined ( $ pp [ 1 ] ) ; # CD 0047
$ hash - > { helper } { wolSetValue } = $ pp [ 2 ] if defined ( $ pp [ 2 ] ) ; # CD 0047
2014-07-06 22:13:50 +00:00
next ;
2015-03-19 20:32:45 +00:00
} elsif ( $ _ =~ /^(PRESENCE:)(.*)/ ) { # CD 0007
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2018-04-22 19:53:00 +00:00
my @ pp = split ':' , $ 2 ; # CD 0047
$ hash - > { PRESENCENAME } = $ pp [ 0 ] ; # CD 0007 CD 0047
$ hash - > { helper } { presenceReading } = $ pp [ 1 ] if defined ( $ pp [ 1 ] ) ; # CD 0047
$ hash - > { helper } { presenceValuePresent } = $ pp [ 2 ] if defined ( $ pp [ 2 ] ) ; # CD 0047
$ hash - > { helper } { presenceValueAbsent } = $ pp [ 3 ] if defined ( $ pp [ 3 ] ) ; # CD 0047
push @ notifyregexp , $ pp [ 0 ] ; # CD 0041
2015-03-19 20:32:45 +00:00
next ; # CD 0007
2014-07-06 22:13:50 +00:00
} elsif ( $ _ =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d{3,5})/ ) {
$ hash - > { IP } = $ 1 ;
$ hash - > { CLIPORT } = $ 2 ;
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ ) {
$ hash - > { IP } = $ 1 ;
$ hash - > { CLIPORT } = 9090 ;
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(USER:)(.*)/ ) {
2017-05-28 19:22:04 +00:00
$ user = $ 2 if ( $ 2 ne 'yes' ) ;
2018-04-22 19:53:00 +00:00
$ hash - > { USERNAME } = 'yes' ;
2017-05-28 19:22:04 +00:00
push @ newDef , 'USER:yes' ;
2014-07-06 22:13:50 +00:00
} elsif ( $ _ =~ /^(PASSWORD:)(.*)/ ) {
2017-05-28 19:22:04 +00:00
$ password = $ 2 if ( $ 2 ne 'yes' ) ;
2018-04-22 19:53:00 +00:00
$ hash - > { PASSWORD } = 'yes' ;
2017-05-28 19:22:04 +00:00
push @ newDef , 'PASSWORD:yes' ;
2014-07-06 22:13:50 +00:00
} else {
2017-05-28 19:22:04 +00:00
push @ newDef , $ _ ;
2014-07-06 22:13:50 +00:00
next ;
}
}
2017-05-28 19:22:04 +00:00
# CD 0031
if ( defined ( $ user ) && defined ( $ password ) ) {
SB_SERVER_storePassword ( $ hash , $ user , $ password ) ;
$ hash - > { DEF } = join ( ' ' , @ newDef ) ;
}
2014-07-06 22:13:50 +00:00
$ hash - > { LASTANSWER } = "none" ;
# used for alive checking of the CLI interface
$ hash - > { ALIVECHECK } = "?" ;
# the status of the CLI connection (on / off)
$ hash - > { CLICONNECTION } = "?" ;
# preset our attributes
if ( ! defined ( $ attr { $ name } { alivetimer } ) ) {
$ attr { $ name } { alivetimer } = 120 ;
}
if ( ! defined ( $ attr { $ name } { doalivecheck } ) ) {
$ attr { $ name } { doalivecheck } = "true" ;
}
if ( ! defined ( $ attr { $ name } { maxfavorites } ) ) {
$ attr { $ name } { maxfavorites } = 30 ;
}
if ( ! defined ( $ attr { $ name } { maxcmdstack } ) ) {
$ attr { $ name } { maxcmdstack } = 200 ;
}
2014-11-09 18:01:21 +00:00
# the port of the HTTP interface as needed for the coverart url
2018-04-22 19:53:00 +00:00
# CD 0049 auf $hash->{helper}{httpport} umgestellt
2014-11-09 18:01:21 +00:00
if ( ! defined ( $ attr { $ name } { httpport } ) ) {
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } = '9000' ;
} else {
$ hash - > { helper } { httpport } = $ attr { $ name } { httpport } ;
2014-11-09 18:01:21 +00:00
}
2014-07-06 22:13:50 +00:00
# Preset our readings if undefined
2017-07-26 19:21:33 +00:00
if ( ! defined ( $ hash - > { OLDDEF } ) ) {
readingsBeginUpdate ( $ hash ) ;
# server on / off
readingsBulkUpdate ( $ hash , "power" , "?" ) ;
# the server version
readingsBulkUpdate ( $ hash , "serverversion" , "?" ) ;
# is the CLI port secured with password?
readingsBulkUpdate ( $ hash , "serversecure" , "?" ) ;
# the maximum number of favorites on the server
readingsBulkUpdate ( $ hash , "favoritestotal" , "?" ) ;
# is a scan in progress
readingsBulkUpdate ( $ hash , "scanning" , "?" ) ;
# the scan in progress
readingsBulkUpdate ( $ hash , "scandb" , "?" ) ;
# the scan already completed
readingsBulkUpdate ( $ hash , "scanprogressdone" , "?" ) ;
# the scan already completed
readingsBulkUpdate ( $ hash , "scanprogresstotal" , "?" ) ;
# did the last scan fail
readingsBulkUpdate ( $ hash , "scanlastfailed" , "?" ) ;
# number of players connected to us
readingsBulkUpdate ( $ hash , "players" , "?" ) ;
# number of players connected to mysqueezebox
readingsBulkUpdate ( $ hash , "players_mysb" , "?" ) ;
# number of players connected to other servers in our network
readingsBulkUpdate ( $ hash , "players_other" , "?" ) ;
# number of albums in the database
readingsBulkUpdate ( $ hash , "db_albums" , "?" ) ;
# number of artists in the database
readingsBulkUpdate ( $ hash , "db_artists" , "?" ) ;
# number of songs in the database
readingsBulkUpdate ( $ hash , "db_songs" , "?" ) ;
# number of genres in the database
readingsBulkUpdate ( $ hash , "db_genres" , "?" ) ;
readingsEndUpdate ( $ hash , 0 ) ;
2014-07-06 22:13:50 +00:00
}
# initialize the command stack
$ SB_SERVER_CmdStack { $ name } { first_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { last_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { cnt } = 0 ;
2015-03-19 20:32:45 +00:00
$ hash - > { CMDSTACK } = 0 ; # CD 0007
2014-07-06 22:13:50 +00:00
# assign our IO Device
$ hash - > { DeviceName } = "$hash->{IP}:$hash->{CLIPORT}" ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { pingCounter } = 0 ; # CD 0004
2016-10-01 16:43:31 +00:00
$ hash - > { helper } { lastPRESENCEstate } = '?' ; # CD 0023
2017-06-04 10:03:33 +00:00
$ hash - > { helper } { onAfterAliveCheck } = 0 ; # CD 0038
2015-03-19 20:32:45 +00:00
# CD 0009 set module version, needed for reload
$ hash - > { helper } { SB_SERVER_VERSION } = SB_SERVER_VERSION ;
2017-05-28 19:22:04 +00:00
if ( ! defined ( $ hash - > { OLDDEF } ) ) { # CD 0024
SB_SERVER_LoadSyncGroups ( $ hash ) if ( $ SB_SERVER_hasDataDumper == 1 ) ;
SB_SERVER_LoadServerStates ( $ hash ) if ( $ SB_SERVER_hasDataDumper == 1 ) ;
SB_SERVER_FixSyncGroupNames ( $ hash ) ; # CD 0027
SB_SERVER_UpdateSgReadings ( $ hash ) ; # CD 0027
2017-06-04 10:03:33 +00:00
} else {
# CD 0038
if ( ReadingsVal ( $ name , "state" , "unknown" ) eq "opened" ) {
DevIo_SimpleWrite ( $ hash , "listen 0\n" , 0 ) ;
}
$ hash - > { helper } { disableReconnect } = 1 ;
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
DevIo_Disconnected ( $ hash ) ;
SB_SERVER_setStates ( $ hash , "disconnected" ) ;
2017-05-28 19:22:04 +00:00
}
2014-07-06 22:13:50 +00:00
# open the IO device
2015-03-19 20:32:45 +00:00
my $ ret ;
# CD wait for init_done
if ( $ init_done > 0 ) {
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ; # CD 0007 reconnect immediately after modify
2015-11-07 19:17:27 +00:00
# CD 0016 start
2017-06-04 10:03:33 +00:00
#if( ReadingsVal($name, "state", "unknown") eq "opened" ) { # CD 0038 state statt STATE verwenden
# DevIo_CloseDev( $hash );
# readingsSingleUpdate( $hash, "power", "?", 0 );
# #$hash->{STATE}="disconnected";
#}
2015-11-07 19:17:27 +00:00
# CD 0016 end
2017-06-04 10:03:33 +00:00
if ( defined ( $ hash - > { OLDDEF } ) ) {
InternalTimer ( gettimeofday ( ) + 1 ,
"SB_SERVER_tcb_Alive" ,
"SB_SERVER_Alive:$name" ,
0 ) ;
$ ret = undef ;
} else {
2018-04-22 19:53:00 +00:00
$ ret = SB_SERVER_TryConnect ( $ hash , 0 ) ;
2017-06-04 10:03:33 +00:00
}
2015-03-19 20:32:45 +00:00
}
2014-07-06 22:13:50 +00:00
# do and update of the status
2015-03-19 20:32:45 +00:00
# CD disabled
2017-05-28 19:22:04 +00:00
#InternalTimer( gettimeofday() + 10,
# "SB_SERVER_Alive",
# $hash,
2015-03-19 20:32:45 +00:00
# 0 );
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Define: leaving" ) ;
2017-07-26 19:21:33 +00:00
notifyRegexpChanged ( $ hash , "(" . ( join '|' , @ notifyregexp ) . ")" ) ; # CD 0041
2014-07-06 22:13:50 +00:00
return $ ret ;
}
# ----------------------------------------------------------------------------
# called when deleting a module
# ----------------------------------------------------------------------------
sub SB_SERVER_Undef ( $$ ) {
my ( $ hash , $ arg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Undef: called" ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# no idea what this is for. Copied from 10_TCM.pm
# presumably to notify the clients, that the server is gone
foreach my $ d ( sort keys % defs ) {
2017-05-28 19:22:04 +00:00
if ( ( defined ( $ defs { $ d } ) ) &&
2014-07-06 22:13:50 +00:00
( defined ( $ defs { $ d } { IODev } ) ) &&
( $ defs { $ d } { IODev } == $ hash ) ) {
delete $ defs { $ d } { IODev } ;
}
}
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# terminate the CLI session
DevIo_SimpleWrite ( $ hash , "listen 0\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "exit\n" , 0 ) ;
# close the device
2017-05-28 19:22:04 +00:00
DevIo_CloseDev ( $ hash ) ;
2014-07-06 22:13:50 +00:00
# remove all timers we created
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
return ( undef ) ;
}
# ----------------------------------------------------------------------------
# Shutdown function - called before fhem shuts down
# ----------------------------------------------------------------------------
sub SB_SERVER_Shutdown ( $$ ) {
my ( $ hash , $ dev ) = @ _ ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Shutdown: called" ) ;
# terminate the CLI session
DevIo_SimpleWrite ( $ hash , "listen 0\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "exit\n" , 0 ) ;
# close the device
2017-05-28 19:22:04 +00:00
DevIo_CloseDev ( $ hash ) ;
2014-07-06 22:13:50 +00:00
# remove all timers we created
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2014-07-06 22:13:50 +00:00
return ( undef ) ;
}
# ----------------------------------------------------------------------------
# ReadyFn - called when?
# ----------------------------------------------------------------------------
sub SB_SERVER_Ready ( $ ) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2015-03-19 20:32:45 +00:00
#Log3( $hash, 4, "SB_SERVER_Ready: called" );
# check for bad/missing password
if ( defined ( $ hash - > { helper } { SB_SERVER_LMS_Status } ) ) {
if ( time ( ) - ( $ hash - > { helper } { SB_SERVER_LMS_Status } ) < 2 ) {
2017-05-28 19:22:04 +00:00
if ( ( $ hash - > { USERNAME } ne "?" ) &&
2015-03-19 20:32:45 +00:00
( $ hash - > { PASSWORD } ne "?" ) ) {
$ hash - > { LASTANSWER } = 'invalid username or password ?' ;
2017-06-04 10:03:33 +00:00
Log ( 1 , "SB_SERVER_Ready($name): invalid username or password ?" ) ;
2015-03-19 20:32:45 +00:00
} else {
$ hash - > { LASTANSWER } = 'missing username and password ?' ;
2017-06-04 10:03:33 +00:00
Log ( 1 , "SB_SERVER_Ready($name): missing username and password ?" ) ;
2015-03-19 20:32:45 +00:00
}
$ hash - > { NEXT_OPEN } = time ( ) + 60 ;
}
delete ( $ hash - > { helper } { SB_SERVER_LMS_Status } ) ;
}
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# we need to re-open the device
2017-06-04 10:03:33 +00:00
if ( ReadingsVal ( $ name , "state" , "unknown" ) eq "disconnected" ) { # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
if ( ( ReadingsVal ( $ name , "power" , "on" ) eq "on" ) ||
( ReadingsVal ( $ name , "power" , "on" ) eq "?" ) ) {
# obviously the first we realize the Server is off
# clean up first
2017-06-04 10:03:33 +00:00
if ( $ hash - > { helper } { onAfterAliveCheck } == 0 ) { # CD 0038
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
2017-05-28 19:22:04 +00:00
2017-06-04 10:03:33 +00:00
$ hash - > { CLICONNECTION } = "off" ; # CD 0007
2015-03-19 20:32:45 +00:00
2017-06-04 10:03:33 +00:00
# and signal to our clients
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
}
2015-03-19 20:32:45 +00:00
}
# CD added init_done
if ( $ init_done > 0 ) {
# CD 0007 faster reconnect after WOL, use PRESENCE
my $ reconnect = 0 ;
if ( defined ( $ hash - > { helper } { WOLFastReconnectUntil } ) ) {
$ hash - > { TIMEOUT } = 1 ;
if ( time ( ) > $ hash - > { helper } { WOLFastReconnectNext } ) {
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ;
$ hash - > { helper } { WOLFastReconnectNext } = time ( ) + 15 ;
$ reconnect = 1 ;
}
if ( time ( ) > $ hash - > { helper } { WOLFastReconnectUntil } ) {
delete ( $ hash - > { TIMEOUT } ) ;
delete ( $ hash - > { helper } { WOLFastReconnectUntil } ) ;
delete ( $ hash - > { helper } { WOLFastReconnectNext } ) ;
}
}
2018-04-22 19:53:00 +00:00
if ( ReadingsVal ( $ hash - > { PRESENCENAME } , $ hash - > { helper } { presenceReading } , $ hash - > { helper } { presenceValuePresent } ) eq $ hash - > { helper } { presenceValuePresent } ) { # CD 0047 erweitert
2015-03-19 20:32:45 +00:00
$ reconnect = 1 ;
}
2017-06-04 10:03:33 +00:00
if ( ( $ reconnect == 1 ) && ( ! defined ( $ hash - > { helper } { disableReconnect } ) ) ) {
2018-04-22 19:53:00 +00:00
return ( SB_SERVER_TryConnect ( $ hash , 1 ) ) ;
2015-03-19 20:32:45 +00:00
} else {
return undef ;
}
} else {
return undef ;
}
2014-07-06 22:13:50 +00:00
}
}
# ----------------------------------------------------------------------------
2017-05-28 19:22:04 +00:00
# Get functions
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
sub SB_SERVER_Get ( $@ ) {
my ( $ hash , @ a ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Get: called" ) ;
if ( @ a != 2 ) {
return ( "\"get $name\" needs one parameter" ) ;
}
return ( "?" ) ;
}
# ----------------------------------------------------------------------------
2017-05-28 19:22:04 +00:00
# Attr functions
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
sub SB_SERVER_Attr ( @ ) {
my $ cmd = shift ( @ _ ) ;
my $ name = shift ( @ _ ) ;
2015-11-07 19:17:27 +00:00
my $ hash = $ defs { $ name } ;
2014-07-06 22:13:50 +00:00
my @ args = @ _ ;
2014-07-23 09:01:39 +00:00
Log ( 4 , "SB_SERVER_Attr($name): called with @args" ) ;
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( $ args [ 0 ] eq "alivetimer" ) {
if ( $ cmd eq "set" ) {
2016-10-01 16:43:31 +00:00
# CD 0021 start
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
InternalTimer ( gettimeofday ( ) + $ args [ 1 ] ,
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2016-10-01 16:43:31 +00:00
0 ) ;
# CD 0021 end
2015-11-07 19:17:27 +00:00
}
2017-05-28 19:22:04 +00:00
} elsif ( $ args [ 0 ] eq "httpport" ) {
2018-04-22 19:53:00 +00:00
# CD 0015 bei Änderung des Ports diesen an Clients schicken
2017-05-28 19:22:04 +00:00
if ( $ cmd eq "set" ) {
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } = $ args [ 1 ] ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2015-11-07 19:17:27 +00:00
"IP " . $ hash - > { IP } . ":" .
$ args [ 1 ] ) ;
2018-04-22 19:53:00 +00:00
} elsif ( $ cmd eq 'del' ) {
DevIo_SimpleWrite ( $ hash , "pref httpport ?\n" , 0 ) ; # CD 0049
2015-11-07 19:17:27 +00:00
}
2017-05-28 19:22:04 +00:00
} elsif ( $ args [ 0 ] eq "enablePlugins" ) {
2018-04-22 19:53:00 +00:00
return "$name: device is disabled, modifying enablePlugins is not possible" if ( IsDisabled ( $ name ) ) ; # CD 0046
# CD 0070 bei Änderung Status abfragen
2017-05-28 19:22:04 +00:00
if ( $ cmd eq "set" ) {
if ( $ init_done > 0 ) {
if ( defined ( $ hash - > { helper } { apps } ) ) {
my @ enabledApps = split ( ',' , $ args [ 1 ] ) ;
foreach my $ app ( @ enabledApps ) {
if ( defined ( $ hash - > { helper } { apps } { $ app } ) ) {
DevIo_SimpleWrite ( $ hash , ( $ hash - > { helper } { apps } { $ app } { cmd } ) . " items 0 200\n" , 0 ) ;
}
}
}
}
} else {
if ( defined ( $ hash - > { helper } { apps } ) ) {
my @ enabledApps = split ( ',' , AttrVal ( $ name , 'enablePlugins' , '' ) ) ;
foreach my $ app ( @ enabledApps ) {
if ( defined ( $ hash - > { helper } { apps } { $ app } ) ) {
SB_SERVER_Broadcast ( $ hash , "PLAYLISTS" , "FLUSH " . ( $ hash - > { helper } { apps } { $ app } { cmd } ) , undef ) ;
SB_SERVER_Broadcast ( $ hash , "FAVORITES" , "FLUSH " . ( $ hash - > { helper } { apps } { $ app } { cmd } ) , undef ) ;
}
}
}
delete ( $ hash - > { helper } { apps } ) if ( defined ( $ hash - > { helper } { apps } ) ) ;
delete ( $ hash - > { helper } { appcmd } ) if ( defined ( $ hash - > { helper } { appcmd } ) ) ;
DevIo_SimpleWrite ( $ hash , "apps 0 200\n" , 0 ) ;
}
2018-04-22 19:53:00 +00:00
# CD 0046 start
} elsif ( $ args [ 0 ] eq 'disable' ) {
if ( $ cmd eq 'set' ) {
if ( $ args [ 1 ] eq '1' ) {
DevIo_SimpleWrite ( $ hash , 'listen 0\n' , 0 ) ;
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
DevIo_Disconnected ( $ hash ) ;
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ;
$ hash - > { CLICONNECTION } = 'off' ;
SB_SERVER_setStates ( $ hash , 'disabled' ) ;
} else {
if ( $ hash - > { CLICONNECTION } ne 'on' ) {
SB_SERVER_setStates ( $ hash , 'disconnected' ) ;
if ( ! defined ( $ hash - > { helper } { disableReconnect } ) ) {
readingsSingleUpdate ( $ hash , 'power' , 'off' , 0 ) ;
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ;
InternalTimer ( gettimeofday ( ) + 0.1 ,
"SB_SERVER_tcb_Alive" ,
"SB_SERVER_Alive:$name" ,
0 ) ;
}
}
}
} elsif ( $ cmd eq 'del' ) {
if ( $ hash - > { CLICONNECTION } ne 'on' ) {
SB_SERVER_setStates ( $ hash , 'disconnected' ) ;
if ( ! defined ( $ hash - > { helper } { disableReconnect } ) ) {
readingsSingleUpdate ( $ hash , 'power' , 'off' , 0 ) ;
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ;
InternalTimer ( gettimeofday ( ) + 0.1 ,
"SB_SERVER_tcb_Alive" ,
"SB_SERVER_Alive:$name" ,
0 ) ;
}
}
}
# CD 0046 end
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
return ; # 0033 betateilchen/mahowi
2014-07-06 22:13:50 +00:00
}
# ----------------------------------------------------------------------------
# Set function
# ----------------------------------------------------------------------------
sub SB_SERVER_Set ( $@ ) {
my ( $ hash , @ a ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2014-07-23 09:01:39 +00:00
Log ( 4 , "SB_SERVER_Set($name): called" ) ;
2014-07-06 22:13:50 +00:00
if ( @ a < 2 ) {
return ( "at least one parameter is needed" ) ;
}
$ name = shift ( @ a ) ;
my $ cmd = shift ( @ a ) ;
if ( $ cmd eq "?" ) {
2017-05-28 19:22:04 +00:00
# this one should give us a drop down list
my $ res = "Unknown argument ?, choose one of " .
"on renew:noArg abort:noArg cliraw statusRequest:noArg " ;
$ res . = "rescan:full,playlists " ;
#$res .= "addToFHEMUpdate:noArg removeFromFHEMUpdate:noArg "; # CD 0019
$ res . = "syncGroup " ; # CD 0024
$ res . = "save " ; # CD 0025
#$res .= "getData "; # CD 0030
my $ out = "" ;
if ( defined ( $ hash - > { helper } { savedServerStates } ) ) {
foreach my $ pl ( keys % { $ hash - > { helper } { savedServerStates } } ) {
2018-04-22 19:53:00 +00:00
$ out . = $ pl . "," unless ( $ pl =~ /xxxSgTalkxxx/ ) ; # CD 0027 xxxSgTalkxxx hinzugefügt
2017-05-28 19:22:04 +00:00
}
$ out =~ s/,$// ;
}
$ res . = "recall:$out " ;
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
return ( $ res ) ;
2018-04-22 19:53:00 +00:00
} elsif ( IsDisabled ( $ name ) ) { # CD 0046
return ;
2014-07-06 22:13:50 +00:00
} elsif ( $ cmd eq "on" ) {
if ( ReadingsVal ( $ name , "power" , "off" ) eq "off" ) {
2017-05-28 19:22:04 +00:00
# the server is off, try to reactivate it
if ( $ hash - > { WOLNAME } ne "none" ) {
2018-04-22 19:53:00 +00:00
fhem ( "set $hash->{WOLNAME} $hash->{helper}{wolSetCmd} $hash->{helper}{wolSetValue}" ) ; # CD 0047 Befehl und Wert konfigurierbar
2017-05-28 19:22:04 +00:00
$ hash - > { helper } { WOLFastReconnectUntil } = time ( ) + 120 ; # CD 0007
$ hash - > { helper } { WOLFastReconnectNext } = time ( ) + 30 ; # CD 0007
}
if ( $ hash - > { RCCNAME } ne "none" ) {
fhem ( "set $hash->{RCCNAME} on" ) ;
}
2014-07-06 22:13:50 +00:00
}
2014-07-07 11:02:24 +00:00
} elsif ( $ cmd eq "renew" ) {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Set: renew" ) ;
2017-06-04 10:03:33 +00:00
delete $ hash - > { helper } { disableReconnect } if ( defined ( $ hash - > { helper } { disableReconnect } ) ) ; # CD 0038
if ( ReadingsVal ( $ name , "state" , "unknown" ) eq "opened" ) { # CD 0038
if ( ( defined ( $ a [ 0 ] ) ) && ( $ a [ 0 ] eq 'soft' ) ) {
DevIo_SimpleWrite ( $ hash , "listen 1\n" , 0 ) ;
} else {
DevIo_SimpleWrite ( $ hash , "listen 0\n" , 0 ) ;
$ hash - > { helper } { disableReconnect } = 1 ;
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
DevIo_Disconnected ( $ hash ) ;
2018-04-22 19:53:00 +00:00
$ hash - > { CLICONNECTION } = 'off' ; # CD 0046
2017-06-04 10:03:33 +00:00
InternalTimer ( gettimeofday ( ) + 5 ,
"SB_SERVER_tcb_Alive" ,
"SB_SERVER_Alive:$name" ,
0 ) ;
}
} else {
# CD 0038 force open
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ;
SB_SERVER_Alive ( $ hash ) ;
}
2014-07-07 11:02:24 +00:00
} elsif ( $ cmd eq "abort" ) {
2017-06-04 10:03:33 +00:00
DevIo_SimpleWrite ( $ hash , "listen 0\n" , 0 ) if ( ReadingsVal ( $ name , "state" , "unknown" ) eq "opened" ) ;
if ( ( defined ( $ a [ 0 ] ) ) && ( $ a [ 0 ] eq 'soft' ) ) {
} else {
$ hash - > { helper } { disableReconnect } = 1 ;
DevIo_Disconnected ( $ hash ) ;
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2018-04-22 19:53:00 +00:00
$ hash - > { CLICONNECTION } = 'off' ; # CD 0046
2017-06-04 10:03:33 +00:00
}
2014-07-07 11:02:24 +00:00
} elsif ( $ cmd eq "statusRequest" ) {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Set: statusRequest" ) ;
DevIo_SimpleWrite ( $ hash , "version ?\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "serverstatus 0 200\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "favorites items 0 " .
AttrVal ( $ name , "maxfavorites" , 100 ) . " want_url:1\n" , # CD 0009 url mit abfragen
0 ) ;
DevIo_SimpleWrite ( $ hash , "playlists 0 200\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "alarm playlists 0 300\n" , 0 ) ; # CD 0011
2018-04-22 19:53:00 +00:00
DevIo_SimpleWrite ( $ hash , "pref httpport ?\n" , 0 ) ; # CD 0049
2017-05-28 19:22:04 +00:00
# CD 0032 start
DevIo_SimpleWrite ( $ hash , "apps 0 200\n" , 0 ) ;
if ( defined ( $ hash - > { helper } { apps } ) ) {
my @ enabledApps = split ( ',' , AttrVal ( $ name , 'enablePlugins' , '' ) ) ;
foreach my $ app ( @ enabledApps ) {
if ( defined ( $ hash - > { helper } { apps } { $ app } ) ) {
DevIo_SimpleWrite ( $ hash , ( $ hash - > { helper } { apps } { $ app } { cmd } ) . " items 0 200\n" , 0 ) ;
}
}
}
# CD 0032 end
2014-07-07 11:02:24 +00:00
} elsif ( $ cmd eq "cliraw" ) {
2015-11-07 19:17:27 +00:00
# write raw messages to the CLI interface per player
my $ v = join ( " " , @ a ) ;
2017-05-28 19:22:04 +00:00
$ v . = "\n" ;
Log3 ( $ hash , 5 , "SB_SERVER_Set: cliraw: $v " ) ;
2018-04-22 19:53:00 +00:00
DevIo_SimpleWrite ( $ hash , $ v , 0 ) ; # CD 0016 IOWrite in DevIo_SimpleWrite geändert
2014-07-07 11:02:24 +00:00
} elsif ( $ cmd eq "rescan" ) {
2018-04-22 19:53:00 +00:00
DevIo_SimpleWrite ( $ hash , $ cmd . " " . $ a [ 0 ] . "\n" , 0 ) ; # CD 0016 IOWrite in DevIo_SimpleWrite geändert
2016-10-01 16:43:31 +00:00
# CD 0018 start
2017-05-28 19:22:04 +00:00
} elsif ( $ cmd eq "addToFHEMUpdate" ) {
fhem ( "update add https://raw.githubusercontent.com/ChrisD70/FHEM-Modules/master/autoupdate/sb/controls_squeezebox.txt" ) ;
} elsif ( $ cmd eq "removeFromFHEMUpdate" ) {
fhem ( "update delete https://raw.githubusercontent.com/ChrisD70/FHEM-Modules/master/autoupdate/sb/controls_squeezebox.txt" ) ;
2016-10-01 16:43:31 +00:00
# CD 0018 end
2017-05-28 19:22:04 +00:00
# CD 0024 start
} elsif ( $ cmd eq "syncGroup" ) {
return ( "at least one parameter is needed" ) if ( @ a < 1 ) ;
my $ updateReadings = 0 ;
my $ subcmd = shift ( @ a ) ;
if ( $ subcmd eq 'addp' ) {
return ( "not enough parameters" ) if ( @ a < 2 ) ;
return ( "too many parameters" ) if ( @ a > 2 ) ; # CD 0027
my $ players = $ a [ 0 ] ;
my $ statename = $ a [ 1 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
SB_SERVER_BuildPlayerList ( $ hash ) if ( ! defined ( $ hash - > { helper } { players } ) ) ;
if ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) {
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } = '' ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } = '' ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } = '' ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { c } = 1 ;
$ updateReadings = 1 ;
}
for my $ pl ( split ( "," , $ players ) ) {
my $ found = 0 ;
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
if ( ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } )
or ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { lmsname } ) ) {
$ found = 1 ;
last ;
}
}
}
}
if ( $ found == 0 ) {
if ( defined ( $ hash - > { helper } { players } ) && defined ( $ hash - > { helper } { players } { $ pl } ) ) {
my $ c = $ hash - > { helper } { syncGroups } { $ statename } { 0 } { c } ;
$ hash - > { helper } { syncGroups } { $ statename } { $ c } { fhemname } = $ hash - > { helper } { players } { $ pl } { fhemname } ;
$ hash - > { helper } { syncGroups } { $ statename } { $ c } { lmsname } = $ hash - > { helper } { players } { $ pl } { lmsname } ;
$ hash - > { helper } { syncGroups } { $ statename } { $ c } { mac } = $ hash - > { helper } { players } { $ pl } { mac } ;
if ( $ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } eq '' ) {
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } = $ hash - > { helper } { players } { $ pl } { fhemname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } = $ hash - > { helper } { players } { $ pl } { lmsname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } = $ hash - > { helper } { players } { $ pl } { mac } ;
}
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { c } += 1 ;
$ updateReadings = 1 ; # CD 0028
}
}
}
} elsif ( $ subcmd eq 'removep' ) {
return ( "not enough parameters" ) if ( @ a < 2 ) ;
return ( "too many parameters" ) if ( @ a > 2 ) ; # CD 0027
my $ players = $ a [ 0 ] ;
my $ statename = $ a [ 1 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
if ( ( ! defined ( $ hash - > { helper } { syncGroups } ) ) || ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ) {
return ( "sync group $statename not found" ) ;
}
SB_SERVER_BuildPlayerList ( $ hash ) if ( ! defined ( $ hash - > { helper } { players } ) ) ;
for my $ pl ( split ( "," , $ players ) ) {
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
if ( ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } )
or ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { lmsname } ) ) {
if ( $ e eq '0' ) {
# master ?
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } = '' ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } = '' ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } = '' ;
} else {
delete ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ;
}
$ updateReadings = 1 ; # CD 0028
}
}
}
}
}
# neuen Master suchen ?
if ( $ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } eq '' ) {
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { lmsname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } ;
last ;
}
}
}
} elsif ( $ subcmd eq 'masterp' ) {
return ( "not enough parameters" ) if ( @ a < 2 ) ;
return ( "too many parameters" ) if ( @ a > 2 ) ; # CD 0027
my $ pl = $ a [ 0 ] ;
my $ statename = $ a [ 1 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
if ( ( ! defined ( $ hash - > { helper } { syncGroups } ) ) || ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ) {
return ( "sync group $statename not found" ) ;
}
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
if ( ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } )
or ( $ pl eq $ hash - > { helper } { syncGroups } { $ statename } { $ e } { lmsname } ) ) {
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { fhemname } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { lmsname } ;
$ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } = $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } ;
$ updateReadings = 1 ;
}
}
}
}
} elsif ( $ subcmd eq 'load' ) {
my $ poweron = 0 ;
if ( $ a [ 0 ] eq 'poweron' ) {
$ poweron = 1 ;
shift ( @ a ) ;
}
return ( "not enough parameters" ) if ( @ a == 0 ) ;
my $ statename = $ a [ 0 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
SB_SERVER_LoadSyncGroup ( $ hash , $ statename , $ poweron ) ; # CD 0027
} elsif ( $ subcmd eq 'delete' ) {
my $ statename = $ a [ 0 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
delete ( $ hash - > { helper } { syncGroups } { $ statename } ) if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ;
delete ( $ defs { $ name } { READINGS } { "sg$statename" } ) if ( defined ( $ defs { $ name } { READINGS } { "sg$statename" } ) ) ; # CD 0027
$ updateReadings = 1 ;
# CD 0027 start
} elsif ( $ subcmd eq 'deleteall' ) {
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ e } ) ) {
delete ( $ hash - > { helper } { syncGroups } { $ e } ) ;
delete ( $ defs { $ name } { READINGS } { "sg$e" } ) if ( defined ( $ defs { $ name } { READINGS } { "sg$e" } ) ) ;
$ updateReadings = 1 ;
}
}
}
} elsif ( $ subcmd eq 'talk' ) {
return "talk already in progress" if ( defined ( $ hash - > { helper } { sgTalkActivePlayer } ) ) ;
my $ poweron = 0 ;
if ( $ a [ 0 ] eq 'poweron' ) {
$ poweron = 1 ;
shift ( @ a ) ;
}
return ( "not enough parameters" ) if ( @ a < 2 ) ;
my $ statename = shift ( @ a ) ;
$ statename =~ s/[,;:]/_/g ;
if ( ( ! defined ( $ hash - > { helper } { syncGroups } ) ) || ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ) {
return ( "sync group $statename not found" ) ;
}
2018-04-22 19:53:00 +00:00
# Zustand speichern, Gruppe laden, talk verzögert an Player absetzen
2017-05-28 19:22:04 +00:00
SB_SERVER_Save ( $ hash , 'xxxSgTalkxxx' ) ;
SB_SERVER_LoadSyncGroup ( $ hash , $ statename , $ poweron ) ;
$ hash - > { helper } { sgTalkPlayers } = ReadingsVal ( $ name , "sg$statename" , "-" ) ;
if ( $ hash - > { helper } { sgTalkPlayers } eq '-' ) {
Log3 ( $ hash , 2 , "SB_SERVER_Set($name): sgtalk: no players found for group $statename" ) ;
return "no players found" ;
}
$ hash - > { helper } { sgTalkActivePlayer } = 'waiting for power on' ;
$ hash - > { helper } { sgTalkData } = join ( ' ' , @ a ) ;
$ hash - > { helper } { sgTalkTimeoutPowerOn } = time ( ) + 3 ;
$ hash - > { helper } { sgTalkGroup } = $ statename ; # CD 0031
RemoveInternalTimer ( "StartTalk:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_StartTalk" ,
"StartTalk:$name" ,
0 ) ;
} elsif ( $ subcmd eq 'resettts' ) {
SB_SERVER_Recall ( $ hash , 'xxxSgTalkxxx del' ) ;
delete $ hash - > { helper } { sgTalkActivePlayer } ;
} elsif ( $ subcmd eq 'fixnames' ) {
SB_SERVER_FixSyncGroupNames ( $ hash ) ;
SB_SERVER_UpdateSgReadings ( $ hash ) ;
# CD 0027 end
# CD 0031 start
} elsif ( $ subcmd eq 'volume' ) {
return ( "not enough parameters" ) if ( @ a != 2 ) ;
my $ statename = $ a [ 0 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
my $ vol = $ a [ 1 ] ;
if ( SB_SERVER_isSyncGroupActive ( $ hash , $ statename ) == 1 ) {
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
fhem ( "set " . $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } . " volume " . $ vol . " nosync" ) ;
}
}
}
} else {
return "Sync group $statename not active" ;
}
# CD 0031 end
# CD 0031 nur zu Testzwecken
} elsif ( $ subcmd eq 'isActive' ) {
return ( "not enough parameters" ) if ( @ a == 0 ) ;
my $ statename = $ a [ 0 ] ;
$ statename =~ s/[,;:]/_/g ; # CD 0027 Sonderzeichen ersetzen
return SB_SERVER_isSyncGroupActive ( $ hash , $ statename ) ;
} else {
return ( "unknown command $subcmd" ) ;
}
# CD 0031 end
# CD 0025 start
if ( $ updateReadings == 1 ) {
SB_SERVER_UpdateSgReadings ( $ hash ) ;
}
# CD 0025 end
# CD 0024 end
# CD 0025 start
} elsif ( $ cmd eq "save" ) {
if ( defined ( $ a [ 0 ] ) ) {
SB_SERVER_Save ( $ hash , $ a [ 0 ] ) ;
} else {
SB_SERVER_Save ( $ hash , "" ) ;
}
} elsif ( $ cmd eq "recall" ) {
if ( defined ( $ a [ 0 ] ) ) {
SB_SERVER_Recall ( $ hash , $ a [ 0 ] ) ;
} else {
SB_SERVER_Recall ( $ hash , "" ) ;
}
# CD 0025 end
# CD 0030 start
} elsif ( $ cmd eq "getData" ) {
return ( "not enough parameters" ) if ( @ a < 3 ) ;
$ hash - > { helper } { getData } { $ a [ 2 ] } { format } = $ a [ 0 ] ;
$ hash - > { helper } { getData } { $ a [ 2 ] } { reading } = $ a [ 1 ] ;
if ( $ a [ 2 ] eq 'artists' ) {
DevIo_SimpleWrite ( $ hash , "artists 0 5000\n" , 0 ) ;
}
# CD 0030 end
2014-07-07 11:02:24 +00:00
} else {
;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
2014-07-07 11:02:24 +00:00
return ( undef ) ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0027 start
sub SB_SERVER_tcb_StartTalk ($) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2017-05-28 19:22:04 +00:00
return unless defined ( $ hash - > { helper } { sgTalkActivePlayer } ) ;
# alle gesynced ?
if ( SB_SERVER_isSyncGroupActive ( $ hash , $ hash - > { helper } { sgTalkGroup } ) == 1 ) {
# eingeschalteten Player suchen
my @ pls = split ( ',' , $ hash - > { helper } { sgTalkPlayers } ) ;
foreach my $ pl ( @ pls ) {
if ( ReadingsVal ( $ pl , 'power' , '0' ) eq 'on' ) {
$ hash - > { helper } { sgTalkActivePlayer } = $ pl ;
my $ phash = $ defs { $ pl } ; # CD 0029
$ phash - > { helper } { sgTalkActive } = 1 ; # CD 0029
fhem "set $pl talk " . $ hash - > { helper } { sgTalkData } ;
last ;
}
}
}
# keinen eingeschalteten Player gefunden
if ( $ hash - > { helper } { sgTalkTimeoutPowerOn } < time ( ) ) {
# kein Player eingeschaltet, abbrechen
Log3 ( $ hash , 1 , "SB_SERVER_tcb_StartTalk($name): timeout waiting for player power on and sync, aborting" ) ;
SB_SERVER_Recall ( $ hash , 'xxxSgTalkxxx del' ) ;
delete $ hash - > { helper } { sgTalkActivePlayer } ;
} else {
# warten...
if ( $ hash - > { helper } { sgTalkActivePlayer } eq 'waiting for power on' ) {
RemoveInternalTimer ( "StartTalk:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.2 ,
"SB_SERVER_tcb_StartTalk" ,
"StartTalk:$name" ,
0 ) ;
}
}
}
# CD 0031 start
sub SB_SERVER_isSyncGroupActive ($$) {
my ( $ hash , $ statename ) = @ _ ;
my $ name = $ hash - > { NAME } ;
if ( ( ! defined ( $ hash - > { helper } { syncGroups } ) ) || ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ) {
return 0 ;
}
my $ master = $ hash - > { helper } { syncGroups } { $ statename } { 0 } { lmsname } ;
my $ isActive = 1 ;
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
$ isActive = 0 if ( InternalVal ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } { fhemname } , 'SYNCMASTERPN' , '?' ) ne $ master )
}
}
}
return $ isActive ;
}
# CD 0031 end
sub SB_SERVER_LoadSyncGroup ($$$) {
my ( $ hash , $ statename , $ poweron ) = @ _ ;
my $ name = $ hash - > { NAME } ;
Log3 ( $ hash , 3 , "SB_SERVER_LoadSyncGroup($name): load: $statename, poweron: $poweron" ) ;
if ( ( ! defined ( $ hash - > { helper } { syncGroups } ) ) || ( ! defined ( $ hash - > { helper } { syncGroups } { $ statename } ) ) ) {
return ( "sync group $statename not found" ) ;
}
# unsync all
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
SB_SERVER_Write ( $ hash , $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } . " sync -\n" , "" ) ;
}
}
}
# sync with new master
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } { $ statename } } ) {
if ( ( $ e ne '' ) && ( $ e ne '0' ) ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } ) ) {
if ( $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } ne $ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } ) {
SB_SERVER_Write ( $ hash , $ hash - > { helper } { syncGroups } { $ statename } { 0 } { mac } . " sync " . $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } . "\n" , "" ) ;
}
SB_SERVER_Write ( $ hash , $ hash - > { helper } { syncGroups } { $ statename } { $ e } { mac } . " power 1\n" , "" ) if ( $ poweron == 1 ) ;
}
}
}
}
sub SB_SERVER_FixSyncGroupNames ($) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ e } ) ) {
if ( $ e =~ /[,;:\s]/ ) {
my $ n = $ e ;
$ n =~ s/[,;:\s]/_/g ;
$ hash - > { helper } { syncGroups } { $ n } = $ hash - > { helper } { syncGroups } { $ e } ;
delete $ hash - > { helper } { syncGroups } { $ e } ;
}
}
}
}
}
sub SB_SERVER_UpdateSgReadings ($) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ sg = '' ;
readingsBeginUpdate ( $ hash ) ;
foreach my $ e ( keys % { $ hash - > { helper } { syncGroups } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ e } ) ) {
$ sg . = "$e," ;
my $ sgd = '' ;
foreach my $ p ( keys % { $ hash - > { helper } { syncGroups } { $ e } } ) {
if ( $ p ne '' ) {
if ( defined ( $ hash - > { helper } { syncGroups } { $ e } { $ p } ) ) {
if ( ( $ hash - > { helper } { syncGroups } { $ e } { $ p } { fhemname } ne $ hash - > { helper } { syncGroups } { $ e } { 0 } { fhemname } ) || ( $ p eq '0' ) ) { # CD 0029
$ sgd . = $ hash - > { helper } { syncGroups } { $ e } { $ p } { fhemname } . "," ;
}
}
}
}
$ sgd =~ s/,$// ;
if ( $ sgd eq '' ) {
delete ( $ defs { $ name } { READINGS } { "sg$e" } ) if ( defined ( $ defs { $ name } { READINGS } { "sg$e" } ) ) ;
} else {
if ( ReadingsVal ( $ name , "sg$e" , "x" ) ne $ sgd ) {
readingsBulkUpdate ( $ hash , "sg$e" , $ sgd ) ;
}
}
}
}
}
$ sg =~ s/,$// ;
if ( $ sg eq '' ) {
delete ( $ defs { $ name } { READINGS } { "syncGroups" } ) if ( defined ( $ defs { $ name } { READINGS } { "syncGroups" } ) ) ;
} else {
readingsBulkUpdate ( $ hash , "syncGroups" , $ sg ) ;
}
readingsEndUpdate ( $ hash , 1 ) ;
}
# CD 0027 end
2014-07-07 11:02:24 +00:00
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# Read
# called from the global loop, when the select for hash->{FD} reports data
# ----------------------------------------------------------------------------
sub SB_SERVER_Read ( $ ) {
my ( $ hash ) = @ _ ;
2014-07-23 09:01:39 +00:00
my $ name = $ hash - > { NAME } ;
2014-07-06 22:13:50 +00:00
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2016-10-01 16:43:31 +00:00
#my $start = time; # CD 0019
2017-05-28 19:22:04 +00:00
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Read($name): called" ) ;
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 5 , "+++++++++++++++++++++++++++++++++++++++++++++++++++++" ) ;
Log3 ( $ hash , 5 , "New Squeezebox Server Read cycle starts here" ) ;
Log3 ( $ hash , 5 , "+++++++++++++++++++++++++++++++++++++++++++++++++++++" ) ;
my $ buf = DevIo_SimpleRead ( $ hash ) ;
if ( ! defined ( $ buf ) ) {
2016-10-01 16:43:31 +00:00
return ( "" ) ;
2014-07-06 22:13:50 +00:00
}
# if we have data, the server is on again
if ( ReadingsVal ( $ name , "power" , "off" ) ne "on" ) {
2016-10-01 16:43:31 +00:00
readingsSingleUpdate ( $ hash , "power" , "on" , 1 ) ;
if ( defined ( $ SB_SERVER_CmdStack { $ name } { cnt } ) ) {
my $ maxmsg = $ SB_SERVER_CmdStack { $ name } { cnt } ;
my $ out ;
for ( my $ n = 0 ; $ n <= $ maxmsg ; $ n + + ) {
$ out = SB_SERVER_CMDStackPop ( $ hash ) ;
if ( $ out ne "empty" ) {
DevIo_SimpleWrite ( $ hash , $ out , 0 ) ;
2017-05-28 19:22:04 +00:00
}
2016-10-01 16:43:31 +00:00
}
}
#Log3( $hash, 5, "SB_SERVER_Read($name): please implement the " . # CD 0009 Meldung deaktiviert
# "sending of the CMDStack." ); # CD 0009 Meldung deaktiviert
2014-07-06 22:13:50 +00:00
}
2016-10-01 16:43:31 +00:00
#my $t1 = time; # CD 0020
2014-07-06 22:13:50 +00:00
# if there are remains from the last time, append them now
$ buf = $ hash - > { PARTIAL } . $ buf ;
$ buf = uri_unescape ( $ buf ) ;
2015-05-08 20:52:02 +00:00
Log3 ( $ hash , 6 , "SB_SERVER_Read: the buf: $buf" ) ; # CD TEST 6
2014-07-06 22:13:50 +00:00
2016-10-01 16:43:31 +00:00
# CD 0021 start - Server lebt noch, alivetimer neu starten
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) +
2016-10-01 16:43:31 +00:00
AttrVal ( $ name , "alivetimer" , 10 ) ,
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2016-10-01 16:43:31 +00:00
0 ) ;
# CD 0021 end
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
#my $t2 = time; # CD 0020
2014-07-06 22:13:50 +00:00
# if we have received multiline commands, they are split by \n
my @ cmds = split ( "\n" , $ buf ) ;
# check for last element in string
my $ lastchr = substr ( $ buf , - 1 , 1 ) ;
if ( $ lastchr ne "\n" ) {
2016-10-01 16:43:31 +00:00
#ups, the return doesn't seem to be complete
$ hash - > { PARTIAL } = $ cmds [ $# cmds ] ;
# and remove the last element
pop ( @ cmds ) ;
Log3 ( $ hash , 5 , "SB_SERVER_Read: uncomplete command received" ) ;
2014-07-06 22:13:50 +00:00
} else {
2016-10-01 16:43:31 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Read: complete command received" ) ;
$ hash - > { PARTIAL } = "" ;
2014-07-06 22:13:50 +00:00
}
2016-10-01 16:43:31 +00:00
#my $t3 = time; # CD 0020
2014-07-06 22:13:50 +00:00
# and dispatch the rest
foreach ( @ cmds ) {
2018-04-22 19:53:00 +00:00
my $ t31 = time ; # CD 0020
2016-10-01 16:43:31 +00:00
# double check complete line
my $ lastchar = substr ( $ _ , - 1 ) ;
SB_SERVER_DispatchCommandLine ( $ hash , $ _ ) ;
# CD 0020 start
2018-04-22 19:53:00 +00:00
if ( ( time - $ t31 ) > 0.5 ) {
Log3 ( $ hash , 3 , "SB_SERVER_Read($name), time:" . int ( ( time - $ t31 ) * 1000 ) . "ms cmd: " . $ _ ) ;
}
2016-10-01 16:43:31 +00:00
# CD 0020 end
2014-07-06 22:13:50 +00:00
}
2016-10-01 16:43:31 +00:00
#my $t4 = time; # CD 0020
2015-03-19 20:32:45 +00:00
# CD 0009 check for reload of newer version
$ hash - > { helper } { SB_SERVER_VERSION } = 0 if ( ! defined ( $ hash - > { helper } { SB_SERVER_VERSION } ) ) ; # CD 0012
if ( $ hash - > { helper } { SB_SERVER_VERSION } ne SB_SERVER_VERSION )
{
Log3 ( $ hash , 1 , "SB_SERVER_Read: SB_SERVER_VERSION changed from " . $ hash - > { helper } { SB_SERVER_VERSION } . " to " . SB_SERVER_VERSION ) ; # CD 0012
$ hash - > { helper } { SB_SERVER_VERSION } = SB_SERVER_VERSION ;
DevIo_SimpleWrite ( $ hash , "version ?\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "serverstatus 0 200\n" , 0 ) ;
2017-05-28 19:22:04 +00:00
DevIo_SimpleWrite ( $ hash , "favorites items 0 " .
2015-03-19 20:32:45 +00:00
AttrVal ( $ name , "maxfavorites" , 100 ) . " want_url:1\n" , # CD 0009 url mit abfragen
0 ) ;
2018-04-22 19:53:00 +00:00
DevIo_SimpleWrite ( $ hash , "pref httpport ?\n" , 0 ) ; # CD 0049
2015-03-19 20:32:45 +00:00
DevIo_SimpleWrite ( $ hash , "playlists 0 200\n" , 0 ) ;
}
# CD 0009 end
2016-10-01 16:43:31 +00:00
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 5 , "+++++++++++++++++++++++++++++++++++++++++++++++++++++" ) ;
Log3 ( $ hash , 5 , "Squeezebox Server Read cycle ends here" ) ;
Log3 ( $ hash , 5 , "+++++++++++++++++++++++++++++++++++++++++++++++++++++" ) ;
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
# CD 0019 start
#my $end = time;
#if (($end - $start)>1) {
# Log3( $hash, 0, "SB_SERVER_Read($name), times: ".int(($t1 - $start)*1000)." ".int(($t2 - $t1)*1000)." ".int(($t3 - $t2)*1000)." ".int(($t4 - $t3)*1000)." ".int(($end - $start)*1000)." nCmds: ".$#cmds );
#}
# CD 0019 end
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
return ( undef ) ;
}
2017-05-28 19:22:04 +00:00
# CD 0027 start
sub SB_SERVER_tcb_RecallAfterTalk ($) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
return unless defined ( $ hash - > { helper } { sgTalkActivePlayer } ) ;
SB_SERVER_Recall ( $ hash , 'xxxSgTalkxxx del' ) ;
delete $ hash - > { helper } { sgTalkActivePlayer } ;
}
# CD 0027 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# called by the clients to send data
# ----------------------------------------------------------------------------
sub SB_SERVER_Write ( $$$ ) {
my ( $ hash , $ fn , $ msg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2015-05-08 20:52:02 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Write($name): called with FN:$fn" ) ; # unless($fn=~m/\?/); # CD TEST 4
2014-07-23 09:01:39 +00:00
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2014-07-06 22:13:50 +00:00
if ( ! defined ( $ fn ) ) {
return ( undef ) ;
}
if ( defined ( $ msg ) ) {
Log3 ( $ hash , 4 , "SB_SERVER_Write: MSG:$msg" ) ;
}
2018-04-22 19:53:00 +00:00
# CD 0012 fhemrelay Meldungen nicht an den LMS schicken sondern direkt an Dispatch übergeben
2015-03-19 20:32:45 +00:00
if ( $ fn =~ m/fhemrelay/ ) {
2017-05-28 19:22:04 +00:00
# CD 0027 start
if ( $ fn =~ m/ttsdone/ ) {
my @ a = split ( ' ' , $ fn ) ;
# sg talk auf Player aktiv ?
if ( defined ( $ hash - > { helper } { sgTalkActivePlayer } ) ) {
if ( $ a [ 0 ] eq $ hash - > { helper } { sgTalkActivePlayer } ) {
2018-04-22 19:53:00 +00:00
# recall auslösen
2017-05-28 19:22:04 +00:00
RemoveInternalTimer ( "RecallAfterTalk:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_RecallAfterTalk" ,
"RecallAfterTalk:$name" ,
0 ) ;
}
}
} else {
# CD 0027 end
SB_SERVER_DispatchCommandLine ( $ hash , $ fn ) ;
}
2015-03-19 20:32:45 +00:00
return ( undef ) ;
}
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
if ( ReadingsVal ( $ name , "serversecure" , "0" ) eq "1" ) {
if ( ( $ hash - > { USERNAME } ne "?" ) && ( $ hash - > { PASSWORD } ne "?" ) ) {
2015-03-19 20:32:45 +00:00
# we need to send username and password first
2014-07-06 22:13:50 +00:00
} else {
2017-05-28 19:22:04 +00:00
my $ retmsg = "SB_SERVER_Write: Server needs username and " .
"password but you did not specify those. No sending" ;
2014-07-06 22:13:50 +00:00
Log3 ( $ hash , 1 , $ retmsg ) ;
return ( $ retmsg ) ;
}
}
if ( ReadingsVal ( $ name , "power" , "on" ) eq "on" ) {
DevIo_SimpleWrite ( $ hash , "$fn" , 0 ) ;
} else {
# we are off, so save the command for later
# if maxcmdstack is 0, the function is turned off
if ( AttrVal ( $ name , "maxcmdstack" , 100 ) > 0 ) {
SB_SERVER_CMDStackPush ( $ hash , $ fn ) ;
}
}
}
# ----------------------------------------------------------------------------
# Initialisation of the CLI connection
# ----------------------------------------------------------------------------
sub SB_SERVER_DoInit ( $ ) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_DoInit($name): called" ) ;
2014-07-06 22:13:50 +00:00
if ( ! $ hash - > { TCPDev } ) {
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 2 , "SB_SERVER_DoInit: no TCPDev available?" ) ; # CD 0009 level 5->2
2017-05-28 19:22:04 +00:00
DevIo_CloseDev ( $ hash ) ;
2014-07-06 22:13:50 +00:00
}
2017-06-04 10:03:33 +00:00
my $ state = ReadingsVal ( $ name , "state" , "unknown" ) ;
Log3 ( $ hash , 3 , "SB_SERVER_DoInit($name): state: " . $ state . " power: " . ReadingsVal ( $ name , "power" , "X" ) ) ; # CD 0009 level 2 -> 3 # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
2017-06-04 10:03:33 +00:00
if ( $ state eq "disconnected" ) { # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
# server is off after FHEM start, broadcast to clients
if ( ( ReadingsVal ( $ name , "power" , "on" ) eq "on" ) ||
( ReadingsVal ( $ name , "power" , "on" ) eq "?" ) ) {
Log3 ( $ hash , 3 , "SB_SERVER_DoInit($name): " . # CD 0009 level 2 -> 3
"SB-Server in hibernate / suspend?." ) ;
# obviously the first we realize the Server is off
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
# and signal to our clients
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2015-03-19 20:32:45 +00:00
"IP " . $ hash - > { IP } . ":" .
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } ) ;
2015-03-19 20:32:45 +00:00
}
return ( 1 ) ;
2017-06-04 10:03:33 +00:00
} elsif ( $ state eq "opened" ) { # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
$ hash - > { ALIVECHECK } = "?" ;
$ hash - > { CLICONNECTION } = "on" ;
if ( ( ReadingsVal ( $ name , "power" , "on" ) eq "off" ) ||
2017-06-04 10:03:33 +00:00
( ReadingsVal ( $ name , "power" , "on" ) eq "?" ) ||
( $ hash - > { helper } { onAfterAliveCheck } == 1 ) ) { # CD 0038
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_DoInit($name): " . # CD 0009 level 2 -> 3
"SB-Server is back again." ) ;
# CD 0007 cleanup
if ( defined ( $ hash - > { helper } { WOLFastReconnectUntil } ) ) {
delete ( $ hash - > { TIMEOUT } ) ;
delete ( $ hash - > { helper } { WOLFastReconnectUntil } ) ;
delete ( $ hash - > { helper } { WOLFastReconnectNext } ) ;
}
$ hash - > { helper } { pingCounter } = 0 ; # CD 0007
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2015-03-19 20:32:45 +00:00
"IP " . $ hash - > { IP } . ":" .
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } ) ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { doBroadcast } = 1 ; # CD 0007
SB_SERVER_LMS_Status ( $ hash ) ;
if ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "false" ) {
readingsSingleUpdate ( $ hash , "power" , "on" , 1 ) ;
#SB_SERVER_Broadcast( $hash, "SERVER", "ON" ); # CD 0007
return ( 0 ) ;
} elsif ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "true" ) {
# start the alive checking mechanism
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) +
2015-03-19 20:32:45 +00:00
AttrVal ( $ name , "alivetimer" , 10 ) ,
2016-10-01 16:43:31 +00:00
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2015-03-19 20:32:45 +00:00
0 ) ;
return ( 0 ) ;
} else {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 2 , "SB_SERVER_DoInit: doalivecheck has " .
2015-03-19 20:32:45 +00:00
"wrong value" ) ;
return ( 1 ) ;
}
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
}
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
} else {
# what the f...
Log3 ( $ hash , 2 , "SB_SERVER_DoInit: unclear status reported" ) ;
return ( 1 ) ;
2014-07-06 22:13:50 +00:00
}
2018-04-22 19:53:00 +00:00
#Log3( $hash, 3, "SB_SERVER_DoInit: something went wrong!" ); # CD 0008 nur für Testzwecke 0009 deaktiviert
#return(0); # CD 0008 nur für Testzwecke 0009 deaktiviert
2015-03-19 20:32:45 +00:00
return ( 1 ) ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0032 start
# ----------------------------------------------------------------------------
# Parse return of app items query
# ----------------------------------------------------------------------------
sub SB_SERVER_ParseAppResponse ( $$ ) {
my ( $ hash , $ buf ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ appresponse = 0 ;
if ( $ buf =~ m/items/ ) {
my @ data = split ( ' ' , $ buf ) ;
#Log 0,$buf;
if ( defined ( $ hash - > { helper } { appcmd } ) && defined ( $ hash - > { helper } { appcmd } { $ data [ 1 ] } ) ) {
my $ appcmd = $ data [ 1 ] ;
my $ appname = $ hash - > { helper } { appcmd } { $ appcmd } { name } ;
my $ nameactive = 0 ;
my $ save = 0 ;
if ( $ buf =~ m/item_id:/ ) {
# app subitems ...
my $ id = "0" ;
my $ pname = "" ;
my $ dest = 0 ;
my $ broadcastPlaylists = 0 ;
my $ broadcastFavorites = 0 ;
foreach ( @ data ) {
if ( $ _ =~ /^(item_id:)(.*)/ ) {
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } ) && ( $ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } eq $ 2 ) ) {
$ dest = 1 ;
}
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } ) && ( $ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } eq $ 2 ) ) {
$ dest = 2 ;
}
} elsif ( $ _ =~ /^(id:)(.*)/ ) {
# new entry
if ( $ save == 1 ) {
if ( $ dest == 1 ) {
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } ) && defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } ) ) {
$ broadcastPlaylists = 1 if ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } { name } ne $ pname ) ;
} else {
$ broadcastPlaylists = 1 ;
}
$ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } { name } = $ pname ;
}
if ( $ dest == 2 ) {
# Bug in squeezecloud ? Element 3.2 funktioniert nicht
if ( $ appcmd eq 'squeezecloud' ) {
if ( $ id - int ( $ id ) > 0.19 ) {
$ id += 0.1 ;
}
}
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } ) && defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } ) ) {
$ broadcastFavorites = 1 if ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } { name } ne $ pname ) ;
} else {
$ broadcastFavorites = 1 ;
}
$ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } { name } = $ pname ;
}
$ save = 0 ;
}
$ id = $ 2 ;
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(name:)(.*)/ ) {
$ pname = $ 2 ;
$ save = 1 ;
$ nameactive = 1 ;
next ;
} elsif ( $ _ =~ /^(type:)(.*)/ ) {
$ nameactive = 0 ;
} else {
$ pname . = " $_" if ( $ nameactive == 1 ) ;
}
}
if ( $ save == 1 ) {
if ( $ dest == 1 ) {
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } ) && defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } ) ) {
$ broadcastPlaylists = 1 if ( $ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } { name } ne $ pname ) ;
} else {
$ broadcastPlaylists = 1 ;
}
$ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ id } { name } = $ pname ;
}
if ( $ dest == 2 ) {
# Bug in squeezecloud ? Element 3.2 funktioniert nicht
if ( $ appcmd eq 'squeezecloud' ) {
if ( $ id - int ( $ id ) > 0.19 ) {
$ id += 0.1 ;
}
}
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } ) && defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } ) ) {
$ broadcastFavorites = 1 if ( $ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } { name } ne $ pname ) ;
} else {
$ broadcastFavorites = 1 ;
}
$ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ id } { name } = $ pname ;
}
}
if ( $ broadcastPlaylists == 1 ) {
SB_SERVER_Broadcast ( $ hash , "PLAYLISTS" , "FLUSH $appcmd" , undef ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendPlaylists:$name" ) ;
foreach my $ pl ( keys % { $ hash - > { helper } { appcmd } { $ appcmd } { playlists } } ) {
my $ plname = $ hash - > { helper } { appcmd } { $ appcmd } { playlists } { $ pl } { name } ;
$ plname =~ s/ /_/g ;
$ plname =~ s/[^[:ascii:]]//g ;
#$plname=unidecode($plname);
my $ uniquename = SB_SERVER_FavoritesName2UID ( $ plname ) ;
push @ SB_SERVER_PLS , "ADD $plname $pl $uniquename $appcmd" ;
}
if ( scalar ( @ SB_SERVER_PLS ) > 0 ) {
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendPlaylists" ,
"SB_SERVER_tcb_SendPlaylists:$name" ,
0 ) ;
}
}
if ( $ broadcastFavorites == 1 ) {
SB_SERVER_Broadcast ( $ hash , "FAVORITES" , "FLUSH $appcmd" , undef ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendFavorites:$name" ) ;
foreach my $ pl ( keys % { $ hash - > { helper } { appcmd } { $ appcmd } { favorites } } ) {
my $ plname = $ hash - > { helper } { appcmd } { $ appcmd } { favorites } { $ pl } { name } ;
$ plname =~ s/ /_/g ;
$ plname =~ s/[^[:ascii:]]//g ;
#$plname=unidecode($plname);
my $ uniquename = SB_SERVER_FavoritesName2UID ( $ plname ) ;
push @ SB_SERVER_FAVS , "ADD $name $pl $uniquename url $appcmd $plname" ;
}
if ( scalar ( @ SB_SERVER_FAVS ) > 0 ) {
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendFavorites" ,
"SB_SERVER_tcb_SendFavorites:$name" ,
0 ) ;
}
}
} else {
# app items ...
my $ id = 0 ;
my $ iname = "" ;
my $ type = "" ;
my $ isaudio = 0 ;
my $ hasitems = 0 ;
foreach ( @ data ) {
if ( $ _ =~ /^(id:)(.*)/ ) {
# new entry
if ( $ save == 1 ) {
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { name } = $ iname ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { type } = $ type ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { isaudio } = $ isaudio ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { hasitems } = $ hasitems ;
$ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } = $ id if ( $ iname eq 'Playlists' ) ;
$ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } = $ id if ( $ iname eq 'Wiedergabelisten' ) ; # CD 0035
$ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } = $ id if ( $ iname eq 'Listes de lecture' ) ; # CD 0036
$ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } = $ id if ( $ iname eq 'Likes' ) ;
$ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } = $ id if ( $ iname eq 'Favorites' ) ;
$ save = 0 ;
}
$ id = $ 2 ;
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(type:)(.*)/ ) {
$ type = $ 2 ;
$ save = 1 ;
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(isaudio:)(.*)/ ) {
$ isaudio = $ 2 ;
$ save = 1 ;
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(hasitems:)(.*)/ ) {
$ hasitems = $ 2 ;
$ save = 1 ;
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(name:)(.*)/ ) {
$ iname = $ 2 ;
$ save = 1 ;
$ nameactive = 1 ;
next ;
} elsif ( $ _ =~ /^(count:)(.*)/ ) {
if ( $ 2 == 0 ) {
delete $ hash - > { helper } { appcmd } { $ appcmd } { items } if defined ( $ hash - > { helper } { appcmd } { $ appcmd } { items } ) ;
$ save = 0 ;
Log3 ( $ hash , 2 , "SB_SERVER_ParseAppResponse($name): no valid data for $appname: $buf" ) ;
last ;
}
} else {
$ iname . = " $_" if ( $ nameactive == 1 ) ;
}
}
if ( $ save == 1 ) {
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { name } = $ iname ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { type } = $ type ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { isaudio } = $ isaudio ;
$ hash - > { helper } { appcmd } { $ appcmd } { items } { $ id } { hasitems } = $ hasitems ;
$ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } = $ id if ( $ iname eq 'Playlists' ) ;
$ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } = $ id if ( $ iname eq 'Likes' ) ;
$ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } = $ id if ( $ iname eq 'Favorites' ) ;
}
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } ) ) {
DevIo_SimpleWrite ( $ hash , "$appcmd items 0 200 item_id:" . ( $ hash - > { helper } { appcmd } { $ appcmd } { playlistsId } ) . "\n" , 0 ) ;
}
if ( defined ( $ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } ) ) {
DevIo_SimpleWrite ( $ hash , "$appcmd items 0 200 item_id:" . ( $ hash - > { helper } { appcmd } { $ appcmd } { favoritesId } ) . " want_url:1\n" , 0 ) ;
}
}
$ appresponse = 1 ;
}
}
return $ appresponse ;
}
# CD 0032 end
2014-07-06 22:13:50 +00:00
2018-04-22 19:53:00 +00:00
# CD 0044 start
# ----------------------------------------------------------------------------
# used for checking, if the string contains a valid IP v4 (decimal) address
# ----------------------------------------------------------------------------
sub SB_SERVER_IsValidIPV4 ( $ ) {
my $ instr = shift ( @ _ ) ;
if ( $ instr =~ m/^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/ )
{
if ( $ 1 <= 255 && $ 2 <= 255 && $ 3 <= 255 && $ 4 <= 255 )
{
return ( 1 ) ;
}
else
{
return ( 0 ) ;
}
}
else
{
return ( 0 ) ;
}
}
# CD 0044 end
2017-07-26 19:21:33 +00:00
# CD 0041 start
# ----------------------------------------------------------------------------
# used for checking if the string contains a valid MAC adress
# ----------------------------------------------------------------------------
sub SB_SERVER_IsValidMAC ( $ ) {
my $ instr = shift ( @ _ ) ;
my $ d = "[0-9A-Fa-f]" ;
my $ dd = "$d$d" ;
if ( $ instr =~ /($dd([:-])$dd(\2$dd){4})/og ) {
if ( $ instr =~ /^(00[:-]){5}(00)$/ ) {
return ( 0 ) ;
} else {
return ( 1 ) ;
}
} else {
return ( 0 ) ;
}
}
# CD 0041 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# Dispatch every single line of commands
# ----------------------------------------------------------------------------
sub SB_SERVER_DispatchCommandLine ( $$ ) {
my ( $ hash , $ buf ) = @ _ ;
my $ name = $ hash - > { NAME } ;
Log3 ( $ hash , 4 , "SB_SERVER_DispatchCommandLine($name): Line:$buf..." ) ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2014-07-06 22:13:50 +00:00
# try to extract the first answer to the SPACE
my $ indx = index ( $ buf , " " ) ;
my $ id1 = substr ( $ buf , 0 , $ indx ) ;
2017-05-28 19:22:04 +00:00
# is the first return value a player ID?
2014-07-06 22:13:50 +00:00
# Player ID is MAC adress, hence : included
my @ id = split ( ":" , $ id1 ) ;
2017-07-26 19:21:33 +00:00
2018-04-22 19:53:00 +00:00
if ( ( SB_SERVER_IsValidMAC ( $ id1 ) == 1 ) || ( SB_SERVER_IsValidIPV4 ( $ id1 ) == 1 ) ) { # CD 0041 SB_SERVER_IsValidMAC verwenden # CD 0044 auf IP-Adresse prüfen
2017-05-28 19:22:04 +00:00
# CD 0032 start
# check for app response
if ( SB_SERVER_ParseAppResponse ( $ hash , $ buf ) == 0 ) {
# we have received a return for a dedicated player
# create the fhem specific unique id
my $ playerid = join ( "" , @ id ) ;
Log3 ( $ hash , 5 , "SB_SERVER_DispatchCommandLine: fhem-id: $playerid" ) ;
# create the commands
my $ cmds = substr ( $ buf , $ indx + 1 ) ;
Log3 ( $ hash , 5 , "SB_SERVER__DispatchCommandLine: commands: $cmds" ) ;
Dispatch ( $ hash , "SB_PLAYER:$playerid:$cmds" , undef ) ;
}
2014-07-06 22:13:50 +00:00
} else {
2017-05-28 19:22:04 +00:00
# that is a server specific command
SB_SERVER_ParseCmds ( $ hash , $ buf ) ;
2014-07-06 22:13:50 +00:00
}
return ( undef ) ;
}
# ----------------------------------------------------------------------------
# parse the server answers that are not intended for players
# ----------------------------------------------------------------------------
sub SB_SERVER_ParseCmds ( $$ ) {
my ( $ hash , $ instr ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): called" ) ;
2014-07-06 22:13:50 +00:00
my @ args = split ( " " , $ instr ) ;
$ hash - > { LASTANSWER } = "@args" ;
my $ cmd = shift ( @ args ) ;
2015-03-19 20:32:45 +00:00
# CD 0007 start
if ( defined ( $ hash - > { helper } { doBroadcast } ) ) {
SB_SERVER_Broadcast ( $ hash , "SERVER" , "ON" ) ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2015-03-19 20:32:45 +00:00
"IP " . $ hash - > { IP } . ":" .
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } ) ;
2015-03-19 20:32:45 +00:00
delete ( $ hash - > { helper } { doBroadcast } ) ;
}
# CD 0007 end
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
if ( $ cmd eq "version" ) {
readingsSingleUpdate ( $ hash , "serverversion" , $ args [ 1 ] , 0 ) ;
if ( ReadingsVal ( $ name , "power" , "off" ) eq "off" ) {
# that also means the server returned from being away
readingsSingleUpdate ( $ hash , "power" , "on" , 1 ) ;
# signal our players
SB_SERVER_Broadcast ( $ hash , "SERVER" , "ON" ) ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2014-11-09 18:01:21 +00:00
"IP " . $ hash - > { IP } . ":" .
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } ) ;
2014-07-06 22:13:50 +00:00
}
} elsif ( $ cmd eq "pref" ) {
if ( $ args [ 0 ] eq "authorize" ) {
readingsSingleUpdate ( $ hash , "serversecure" , $ args [ 1 ] , 0 ) ;
2014-11-09 18:01:21 +00:00
if ( $ args [ 1 ] eq "1" ) {
# username and password is required
2018-04-22 19:53:00 +00:00
# CD 0007 zu spät, login muss als erstes gesendet werden, andernfalls bricht der Server die Verbindung sofort ab
2017-05-28 19:22:04 +00:00
if ( ( $ hash - > { USERNAME } ne "?" ) &&
2014-11-09 18:01:21 +00:00
( $ hash - > { PASSWORD } ne "?" ) ) {
2017-05-28 19:22:04 +00:00
my ( $ user , $ password ) = SB_SERVER_readPassword ( $ hash ) ; # CD 0031
if ( defined ( $ user ) ) {
DevIo_SimpleWrite ( $ hash , "login " .
$ user . " " .
$ password . "\n" ,
0 ) ;
} else {
Log3 ( $ hash , 3 , "SB_SERVER_ParseCmds($name): login " .
"required but no username and password specified" ) ;
}
2014-11-09 18:01:21 +00:00
} else {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_ParseCmds($name): login " .
2014-11-09 18:01:21 +00:00
"required but no username and password specified" ) ;
}
# next step is to wait for the answer of the LMS server
} elsif ( $ args [ 1 ] eq "0" ) {
# no username password required, go ahead directly
2015-03-19 20:32:45 +00:00
#SB_SERVER_LMS_Status( $hash );
2014-11-09 18:01:21 +00:00
} else {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_ParseCmds($name): unkown " .
2014-11-09 18:01:21 +00:00
"result for authorize received. Should be 0 or 1" ) ;
2017-05-28 19:22:04 +00:00
}
2014-11-09 18:01:21 +00:00
}
2018-04-22 19:53:00 +00:00
# CD 0049
if ( $ args [ 0 ] eq "httpport" ) {
if ( defined ( $ args [ 1 ] ) && ( $ args [ 1 ] =~ /^([0-9])*/ ) ) {
if ( ! defined ( AttrVal ( $ name , "httpport" , undef ) ) ) {
$ hash - > { helper } { httpport } = $ args [ 1 ] ;
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
"IP " . $ hash - > { IP } . ":" .
$ hash - > { helper } { httpport } ) ;
}
}
}
2014-11-09 18:01:21 +00:00
} elsif ( $ cmd eq "login" ) {
2017-05-28 19:22:04 +00:00
if ( ( $ args [ 1 ] eq $ hash - > { USERNAME } ) &&
2014-11-09 18:01:21 +00:00
( $ args [ 2 ] eq "******" ) ) {
# login has been succesful, go ahead
SB_SERVER_LMS_Status ( $ hash ) ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
} elsif ( $ cmd eq "fhemalivecheck" ) {
$ hash - > { ALIVECHECK } = "received" ;
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): alivecheck received" ) ;
} elsif ( $ cmd eq "favorites" ) {
if ( $ args [ 0 ] eq "changed" ) {
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): favorites changed" ) ;
# we need to trigger the favorites update here
2017-05-28 19:22:04 +00:00
DevIo_SimpleWrite ( $ hash , "favorites items 0 " .
AttrVal ( $ name , "maxfavorites" , 100 ) .
2015-03-19 20:32:45 +00:00
" want_url:1\n" , 0 ) ; # CD 0009 url mit abfragen
DevIo_SimpleWrite ( $ hash , "alarm playlists 0 300\n" , 0 ) ; # CD 0011
2014-07-06 22:13:50 +00:00
} elsif ( $ args [ 0 ] eq "items" ) {
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): favorites items" ) ;
# the response to our query of the favorites
2017-05-28 19:22:04 +00:00
SB_SERVER_FavoritesParse ( $ hash , join ( " " , @ args ) ) ;
2014-07-06 22:13:50 +00:00
} else {
}
} elsif ( $ cmd eq "serverstatus" ) {
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): server status" ) ;
SB_SERVER_ParseServerStatus ( $ hash , \ @ args ) ;
} elsif ( $ cmd eq "playlists" ) {
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_ParseCmds($name): playlists" ) ;
2018-04-22 19:53:00 +00:00
# CD 0004 Playlisten neu anfragen bei Änderung
2015-03-19 20:32:45 +00:00
if ( ( $ args [ 0 ] eq "rename" ) || ( $ args [ 0 ] eq "delete" ) ) {
DevIo_SimpleWrite ( $ hash , "playlists 0 200\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "alarm playlists 0 300\n" , 0 ) ; # CD 0011
} else {
SB_SERVER_ParseServerPlaylists ( $ hash , \ @ args ) ;
}
} elsif ( $ cmd eq "client" ) {
# CD 0011 start
} elsif ( $ cmd eq "alarm" ) {
if ( $ args [ 0 ] eq "playlists" ) {
SB_SERVER_ParseServerAlarmPlaylists ( $ hash , \ @ args ) ;
}
# CD 0011 end
2015-11-07 19:17:27 +00:00
# CD 0016 start
} elsif ( $ cmd eq "rescan" ) {
if ( $ args [ 0 ] eq "done" ) {
DevIo_SimpleWrite ( $ hash , "serverstatus 0 200\n" , 0 ) ;
2017-05-28 19:22:04 +00:00
# CD 0036 start - refresh favorites and playlists after rescan
DevIo_SimpleWrite ( $ hash , "favorites items 0 " .
AttrVal ( $ name , "maxfavorites" , 100 ) . " want_url:1\n" ,
0 ) ;
DevIo_SimpleWrite ( $ hash , "playlists 0 200\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "alarm playlists 0 300\n" , 0 ) ;
# CD 0036 end
2017-06-22 20:44:53 +00:00
# CD 0039 start
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "scanning" , "no" ) ;
readingsBulkUpdate ( $ hash , "scanprogressdone" , "0" ) ;
readingsBulkUpdate ( $ hash , "scanprogresstotal" , "0" ) ;
readingsBulkUpdate ( $ hash , "scandb" , "?" ) ;
# CD 0040 start
if ( defined $ hash - > { helper } { scanstart } ) {
readingsBulkUpdate ( $ hash , "scanduration" , int ( time ( ) - $ hash - > { helper } { scanstart } ) ) ;
delete $ hash - > { helper } { scanstart } ;
}
# CD 0040 end
readingsEndUpdate ( $ hash , 1 ) ;
} else {
readingsSingleUpdate ( $ hash , "scanning" , "yes" , 1 ) ;
$ hash - > { helper } { scanstart } = time ( ) ; # CD 0040
readingsSingleUpdate ( $ hash , "scanduration" , 0 , 1 ) ; # CD 0040
2015-11-07 19:17:27 +00:00
}
2017-06-22 20:44:53 +00:00
} elsif ( $ cmd eq "scanner" ) {
if ( ( defined $ args [ 0 ] ) && ( $ args [ 0 ] eq 'notify' ) ) {
if ( ( defined $ args [ 1 ] ) && ( substr ( $ args [ 1 ] , 0 , 8 ) eq 'progress' ) ) {
my @ params = split '\|\|' , $ args [ 1 ] ;
if ( ( defined $ params [ 1 ] ) && ( $ params [ 1 ] eq 'importer' ) ) {
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "scanprogressdone" , $ params [ 3 ] ) if defined $ params [ 3 ] ;
readingsBulkUpdate ( $ hash , "scanprogresstotal" , $ params [ 4 ] ) if defined $ params [ 4 ] ;
readingsBulkUpdate ( $ hash , "scandb" , $ params [ 2 ] ) if defined $ params [ 2 ] ;
# CD 0040 start
if ( defined $ hash - > { helper } { scanstart } ) {
readingsBulkUpdate ( $ hash , "scanduration" , int ( time ( ) - $ hash - > { helper } { scanstart } ) ) ;
}
# CD 0040 end
readingsEndUpdate ( $ hash , 1 ) ;
}
}
}
# CD 0039 end
2015-11-07 19:17:27 +00:00
# CD 0016 end
2017-05-28 19:22:04 +00:00
# CD 0030 start
} elsif ( $ cmd eq "artists" ) {
if ( defined ( $ hash - > { helper } { getData } ) && defined ( $ hash - > { helper } { getData } { artists } ) ) {
my ( $ dev , $ reading ) = split ( ':' , $ hash - > { helper } { getData } { artists } { reading } ) ;
if ( $ hash - > { helper } { getData } { artists } { format } eq 'raw' ) {
if ( defined ( $ defs { $ dev } ) ) {
readingsSingleUpdate ( $ defs { $ dev } , $ reading , $ instr , 1 ) ;
}
} else {
my $ artistname = "" ;
my $ jout = "[" ;
my $ lout = "" ;
my $ iout = "" ;
my $ artistid = 0 ;
foreach ( @ args ) {
if ( $ _ =~ /^(artist:)(.*)/ ) {
$ artistname = $ 2 ;
next ;
} elsif ( $ _ =~ /^(id:)([0-9]*)/ ) {
# start new entry
if ( $ artistname ne "" ) {
$ artistname =~ s/\"/\\\"/g ;
$ jout . = "{\"Artist\":\"" . $ artistname . "\",\"Id\":\"" . $ artistid . "\"}," ;
$ lout . = "\"" . $ artistname . "\":" ;
$ iout . = $ artistid . ":" ;
}
$ artistid = $ 2 ;
next ;
} elsif ( $ _ =~ /^(count:)([0-9]*)/ ) {
next ;
} elsif ( $ artistname ne "" ) {
$ artistname =~ s/\"/\\\"/g ;
$ artistname . = " " . $ _ ;
next ;
}
}
if ( $ artistname ne "" ) {
$ jout . = "{\"Artist\":\"" . $ artistname . "\"}" ;
$ lout . = "\"" . $ artistname . "\"" ;
$ iout . = $ artistid ;
}
$ jout . = "]" ;
if ( defined ( $ defs { $ dev } ) ) {
readingsSingleUpdate ( $ defs { $ dev } , $ reading , $ jout , 1 ) if ( $ hash - > { helper } { getData } { artists } { format } eq 'json' ) ;
readingsSingleUpdate ( $ defs { $ dev } , $ reading , $ lout , 1 ) if ( $ hash - > { helper } { getData } { artists } { format } eq 'delimited' ) ;
readingsSingleUpdate ( $ defs { $ dev } , $ reading . "_index" , $ iout , 1 ) if ( $ hash - > { helper } { getData } { artists } { format } eq 'delimited' ) ;
}
}
delete $ hash - > { helper } { getData } { artists } ;
}
# CD 0030 end
# CD 0032 start
} elsif ( $ cmd eq "apps" ) {
my $ save = 0 ;
my $ appcmd = "" ;
my $ appname = "" ;
my $ scansubs = 0 ;
# 1. Mal ?
$ scansubs = 1 unless ( defined ( $ hash - > { helper } { apps } ) ) ;
delete ( $ hash - > { helper } { apps } ) if ( defined ( $ hash - > { helper } { apps } ) ) ;
delete ( $ hash - > { helper } { appcmd } ) if ( defined ( $ hash - > { helper } { appcmd } ) ) ;
foreach ( @ args ) {
if ( $ _ =~ /^(icon:)(.*)/ ) {
# new entry
if ( $ save == 1 ) {
$ hash - > { helper } { apps } { $ appname } { cmd } = $ appcmd ;
$ hash - > { helper } { appcmd } { $ appcmd } { name } = $ appname ;
$ save = 0 ;
}
next ;
} elsif ( $ _ =~ /^(cmd:)(.*)/ ) {
$ appcmd = $ 2 ;
$ save = 1 ;
next ;
} elsif ( $ _ =~ /^(name:)(.*)/ ) {
$ appname = $ 2 ;
$ appname =~ s/\./_/g ;
$ appname =~ s/-/_/g ;
$ save = 1 ;
next ;
} elsif ( $ _ =~ /^(type:)(.*)/ ) {
$ save = 0 if ( $ 2 ne 'xmlbrowser' ) ;
next ;
}
}
if ( $ save == 1 ) {
$ hash - > { helper } { apps } { $ appname } { cmd } = $ appcmd ;
$ hash - > { helper } { appcmd } { $ appcmd } { name } = $ appname ;
}
SB_SERVER_SetAttrList ( $ hash ) ;
if ( defined ( $ hash - > { helper } { apps } ) && ( $ scansubs == 1 ) ) {
my @ enabledApps = split ( ',' , AttrVal ( $ name , 'enablePlugins' , '' ) ) ;
foreach my $ app ( @ enabledApps ) {
if ( defined ( $ hash - > { helper } { apps } { $ app } ) ) {
DevIo_SimpleWrite ( $ hash , ( $ hash - > { helper } { apps } { $ app } { cmd } ) . " items 0 200\n" , 0 ) ;
}
}
}
# CD 0032 end
# CD 0032 end
} else {
# unkown
}
}
# CD 0020 start
sub SB_SERVER_tcb_Alive ($) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
#Log 0,"SB_SERVER_tcb_Alive";
2017-06-04 10:03:33 +00:00
delete ( $ hash - > { helper } { disableReconnect } ) if defined ( $ hash - > { helper } { disableReconnect } ) ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Alive ( $ hash ) ;
2016-10-01 16:43:31 +00:00
}
# CD 0020 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# Alivecheck of the server
# ----------------------------------------------------------------------------
sub SB_SERVER_Alive ( $ ) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2017-06-04 10:03:33 +00:00
my $ state = ReadingsVal ( $ name , "state" , "unknown" ) ; # CD 0038
2014-07-06 22:13:50 +00:00
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2015-03-19 20:32:45 +00:00
# CD 0004 set default to off
#my $rccstatus = "on";
#my $pingstatus = "on";
my $ rccstatus = "off" ;
my $ pingstatus = "off" ;
2014-07-06 22:13:50 +00:00
my $ nexttime = gettimeofday ( ) + AttrVal ( $ name , "alivetimer" , 120 ) ;
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Alive($name): called" ) ; # CD 0006 changed log level from 4 to 2 # CD 0009 level 2->3 # CD 0014 level -> 4
2014-07-06 22:13:50 +00:00
if ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "false" ) {
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Alive($name): alivechecking is off" ) ;
$ rccstatus = "on" ;
$ pingstatus = "on" ;
$ hash - > { helper } { pingCounter } = 0 ; # CD 0004
2014-07-06 22:13:50 +00:00
} else {
2015-03-19 20:32:45 +00:00
# check via the RCC element
if ( $ hash - > { RCCNAME } ne "none" ) {
# an RCC element has been given as argument
$ rccstatus = ReadingsVal ( $ hash - > { RCCNAME } , "state" , "off" ) ;
}
# CD 0007 start
if ( ( $ hash - > { PRESENCENAME } ne "none" )
&& defined ( $ defs { $ hash - > { PRESENCENAME } } )
2018-04-22 19:53:00 +00:00
&& ( ( defined ( $ defs { $ hash - > { PRESENCENAME } } - > { TIMEOUT_NORMAL } )
&& ( ( $ defs { $ hash - > { PRESENCENAME } } - > { TIMEOUT_NORMAL } ) < AttrVal ( $ name , "alivetimer" , 30 ) ) ) || ( GetType ( $ hash - > { PRESENCENAME } , 'x' ) ne 'PRESENCE' ) ) ) {
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Alive($name): using $hash->{PRESENCENAME}" ) ; # CD 0009 level 2->4
2018-04-22 19:53:00 +00:00
if ( ReadingsVal ( $ hash - > { PRESENCENAME } , $ hash - > { helper } { presenceReading } , "xxxxxxx" ) eq $ hash - > { helper } { presenceValuePresent } ) { # CD 0047 erweitert
2015-03-19 20:32:45 +00:00
$ pingstatus = "on" ;
$ hash - > { helper } { pingCounter } = 0 ;
} else {
$ pingstatus = "off" ;
$ hash - > { helper } { pingCounter } = $ hash - > { helper } { pingCounter } + 1 ;
$ nexttime = gettimeofday ( ) + 15 ;
}
} else {
# CD 0007 end
2016-10-01 16:43:31 +00:00
# CD 0021 start
my $ ipp = AttrVal ( $ name , "internalPingProtocol" , "tcp" ) ;
if ( $ ipp eq "none" ) {
2017-06-04 10:03:33 +00:00
if ( $ state eq "disconnected" ) { # CD 0038 state statt STATE verwenden
2016-10-01 16:43:31 +00:00
$ pingstatus = "off" ;
$ hash - > { helper } { pingCounter } = 3 ;
} else {
$ pingstatus = "on" ;
$ hash - > { helper } { pingCounter } = 0 ;
}
2015-03-19 20:32:45 +00:00
} else {
2016-10-01 16:43:31 +00:00
# CD 0021 end
Log3 ( $ hash , 4 , "SB_SERVER_Alive($name): using internal ping" ) ; # CD 0007 # CD 0009 level 2->4
# check via ping
my $ p ;
2018-04-22 19:53:00 +00:00
# CD 0017 eval hinzugefügt, Absturz auf FritzBox, bei Fehler annehmen dass Host verfügbar ist, internalPingProtocol hinzugefügt
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
eval { $ p = Net::Ping - > new ( $ ipp ) ; } ;
if ( $@ ) {
Log3 ( $ hash , 1 , "SB_SERVER_Alive($name): internal ping failed with $@" ) ;
$ pingstatus = "on" ;
$ hash - > { helper } { pingCounter } = 0 ;
} else {
2018-04-22 19:53:00 +00:00
eval { # CD 0048 ungültige Adressen bringen FHEM zum Absturz
if ( $ p - > ping ( $ hash - > { IP } , 2 ) ) {
$ pingstatus = "on" ;
$ hash - > { helper } { pingCounter } = 0 ; # CD 0004
} else {
$ pingstatus = "off" ;
$ hash - > { helper } { pingCounter } = $ hash - > { helper } { pingCounter } + 1 ; # CD 0004
}
} ;
if ( $@ ) {
Log3 ( $ hash , 1 , "SB_SERVER_Alive($name): internal ping failed with $@" ) ;
2016-10-01 16:43:31 +00:00
$ pingstatus = "on" ;
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { pingCounter } = 0 ;
2016-10-01 16:43:31 +00:00
}
# close our ping mechanism again
$ p - > close ( ) ;
}
} # CD 0021
2015-03-19 20:32:45 +00:00
} # CD 0007
2016-10-01 16:43:31 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Alive($name): " . # CD Test 5
2015-03-19 20:32:45 +00:00
"RCC:" . $ rccstatus . " Ping:" . $ pingstatus ) ; # CD 0006 changed log level from 5 to 2 # CD 0009 level 2->3 # CD 0014 level -> 5
2014-07-06 22:13:50 +00:00
}
# set the status of the server accordingly
2015-03-19 20:32:45 +00:00
# CD 0004 added sensitivity to ping
# if( ( $rccstatus eq "on" ) || ( $pingstatus eq "on" ) ) {
if ( ( $ rccstatus eq "on" ) || ( $ hash - > { helper } { pingCounter } < 3 ) ) {
# the server is reachable
if ( ReadingsVal ( $ name , "power" , "on" ) eq "off" ) {
# the first time we see the server being on
Log3 ( $ hash , 3 , "SB_SERVER_Alive($name): " . # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
"SB-Server is back again." ) ;
# first time we realized server is away
2017-06-04 10:03:33 +00:00
if ( $ state eq "disconnected" ) { # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
delete ( $ hash - > { NEXT_OPEN } ) if ( $ hash - > { NEXT_OPEN } ) ; # CD 0007 remove delay for reconnect
2018-04-22 19:53:00 +00:00
SB_SERVER_TryConnect ( $ hash , 1 ) ;
2015-03-19 20:32:45 +00:00
}
readingsSingleUpdate ( $ hash , "power" , "on" , 1 ) ;
$ hash - > { ALIVECHECK } = "?" ;
$ hash - > { CLICONNECTION } = "off" ;
2017-06-04 10:03:33 +00:00
$ hash - > { helper } { onAfterAliveCheck } = 1 ; # CD 0038
2015-03-19 20:32:45 +00:00
# quicker update to capture CLI connection faster
$ nexttime = gettimeofday ( ) + 10 ;
} else { # CD 0005
# check the CLI connection (sub-state)
if ( $ hash - > { ALIVECHECK } eq "waiting" ) {
# ups, we did not receive any answer in the last minutes
# SB Server potentially dead or shut-down
Log3 ( $ hash , 3 , "SB_SERVER_Alive($name): overrun SB-Server dead." ) ; # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
$ hash - > { CLICONNECTION } = "off" ;
2017-06-04 10:03:33 +00:00
$ hash - > { helper } { onAfterAliveCheck } = 0 ; # CD 0038
2015-03-19 20:32:45 +00:00
# signal that to our clients
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
# close the device
# CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
2017-05-28 19:22:04 +00:00
#DevIo_CloseDev( $hash );
DevIo_Disconnected ( $ hash ) ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { pingCounter } = 9999 ; # CD 0007
# CD 0000 start - exit infinite loop after socket has been closed
$ hash - > { ALIVECHECK } = "?" ;
2017-06-04 10:03:33 +00:00
# CD 0038 $hash->{STATE}="disconnected";
2015-03-19 20:32:45 +00:00
# CD 0005 line above does not work (on Linux), fix:
# CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
SB_SERVER_setStates ( $ hash , "disconnected" ) ;
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
# test: clear stack ?
$ SB_SERVER_CmdStack { $ name } { last_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { first_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { cnt } = 0 ;
# CD end
# remove all timers we created
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2015-03-19 20:32:45 +00:00
} else {
if ( $ hash - > { CLICONNECTION } eq "off" ) {
# signal that to our clients
# to be revisited, should only be sent after CLI established
#SB_SERVER_Broadcast( $hash, "SERVER", "ON" ); # CD 0007 disabled, wait for SB_SERVER_LMS_Status
2017-06-04 10:03:33 +00:00
SB_SERVER_LMS_Status ( $ hash ) if ( $ state eq 'opened' ) ; # CD 0038 nur aufrufen wenn Verbindung aufgebaut ist
2015-03-19 20:32:45 +00:00
}
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
$ hash - > { CLICONNECTION } = "on" ;
# just send something to the SB-Server. It will echo it
# if we receive the echo, the server is still alive
$ hash - > { ALIVECHECK } = "waiting" ;
2017-06-04 10:03:33 +00:00
DevIo_SimpleWrite ( $ hash , "fhemalivecheck\n" , 0 ) if ( $ state eq 'opened' ) ; # CD 0038 nur aufrufen wenn Verbindung aufgebaut ist
2015-03-19 20:32:45 +00:00
}
}
2014-07-06 22:13:50 +00:00
} elsif ( ( $ rccstatus eq "off" ) && ( $ pingstatus eq "off" ) ) {
2015-03-19 20:32:45 +00:00
if ( ReadingsVal ( $ name , "power" , "on" ) eq "on" ) {
# the first time we realize the server is off
Log3 ( $ hash , 3 , "SB_SERVER_Alive($name): " . # CD 0004 changed log level from 5 to 2 # CD 0009 level 2->3
"SB-Server in hibernate / suspend?." ) ;
# first time we realized server is away
$ hash - > { CLICONNECTION } = "off" ;
readingsSingleUpdate ( $ hash , "power" , "off" , 1 ) ;
$ hash - > { ALIVECHECK } = "?" ;
# signal that to our clients
SB_SERVER_Broadcast ( $ hash , "SERVER" , "OFF" ) ;
# close the device
# CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
2017-05-28 19:22:04 +00:00
#DevIo_CloseDev( $hash );
DevIo_Disconnected ( $ hash ) ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { pingCounter } = 9999 ; # CD 0007
# CD 0004 set STATE, needed for reconnect
2017-06-04 10:03:33 +00:00
# CD 0038 $hash->{STATE}="disconnected";
2015-03-19 20:32:45 +00:00
# CD 0005 line above does not work (on Linux), fix:
# CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
SB_SERVER_setStates ( $ hash , "disconnected" ) ;
# remove all timers we created
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2015-03-19 20:32:45 +00:00
}
2014-07-06 22:13:50 +00:00
} else {
2015-03-19 20:32:45 +00:00
# we shouldn't end up here
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_Alive($name): funny server status " .
2015-03-19 20:32:45 +00:00
"received. Ping=" . $ pingstatus . " RCC=" . $ rccstatus ) ;
2014-07-06 22:13:50 +00:00
}
# do an update of the status
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
2017-06-04 10:03:33 +00:00
if ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "true" ) {
InternalTimer ( $ nexttime ,
"SB_SERVER_tcb_Alive" ,
"SB_SERVER_Alive:$name" ,
0 ) ;
}
2014-07-06 22:13:50 +00:00
}
# ----------------------------------------------------------------------------
# Broadcast a message to all clients
# ----------------------------------------------------------------------------
sub SB_SERVER_Broadcast ( $$@ ) {
my ( $ hash , $ cmd , $ msg , $ bin ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ iodevhash ;
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_Broadcast($name): called with $cmd - $msg" ) ;
2014-07-06 22:13:50 +00:00
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2014-07-06 22:13:50 +00:00
if ( ! defined ( $ bin ) ) {
$ bin = 0 ;
}
foreach my $ mydev ( keys % defs ) {
2017-05-28 19:22:04 +00:00
# the hash to the IODev as defined at the client
if ( defined ( $ defs { $ mydev } { IODev } ) ) {
$ iodevhash = $ defs { $ mydev } { IODev } ;
} else {
$ iodevhash = undef ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ iodevhash ) ) {
if ( ( defined ( $ defs { $ mydev } { TYPE } ) ) &&
( $ defs { $ mydev } { TYPE } eq "SB_PLAYER" ) ) { # CD 0029 umsortiert
if ( ( defined ( $ iodevhash - > { NAME } ) ) && # CD 0029 umsortiert
( $ iodevhash - > { NAME } eq $ name ) ) {
# we found a valid entry
my $ clienthash = $ defs { $ mydev } ;
my $ namebuf = $ clienthash - > { NAME } ;
SB_PLAYER_RecBroadcast ( $ clienthash , $ cmd , $ msg , $ bin ) ;
}
}
}
2014-07-06 22:13:50 +00:00
}
return ;
}
# ----------------------------------------------------------------------------
# Handle the return for a serverstatus query
# ----------------------------------------------------------------------------
sub SB_SERVER_ParseServerStatus ( $$ ) {
my ( $ hash , $ dataptr ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_ParseServerStatus($name): called " ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# typically the start index being a number
if ( $ dataptr - > [ 0 ] =~ /^([0-9])*/ ) {
shift ( @ { $ dataptr } ) ;
} else {
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerStatus($name): entry is " .
"not the start number" ) ;
return ;
}
# typically the max index being a number
if ( $ dataptr - > [ 0 ] =~ /^([0-9])*/ ) {
shift ( @ { $ dataptr } ) ;
} else {
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerStatus($name): entry is " .
"not the end number" ) ;
return ;
}
my $ datastr = join ( " " , @ { $ dataptr } ) ;
# replace funny stuff
$ datastr =~ s/info total albums/infototalalbums/g ;
$ datastr =~ s/info total artists/infototalartists/g ;
$ datastr =~ s/info total songs/infototalsongs/g ;
$ datastr =~ s/info total genres/infototalgenres/g ;
$ datastr =~ s/sn player count/snplayercount/g ;
$ datastr =~ s/other player count/otherplayercount/g ;
$ datastr =~ s/player count/playercount/g ;
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerStatus($name): data to parse: " .
$ datastr ) ;
my @ data1 = split ( " " , $ datastr ) ;
# the rest of the array should now have the data, we're interested in
readingsBeginUpdate ( $ hash ) ;
my $ addplayers = true ;
my % players ;
my $ currentplayerid = "none" ;
# needed for scanning the MAC Adress
my $ d = "[0-9A-Fa-f]" ;
my $ dd = "$d$d" ;
# needed for scanning the IP adress
my $ e = "[0-9]" ;
my $ ee = "$e$e" ;
2017-05-28 19:22:04 +00:00
my $ nameactive = 0 ;
2017-06-22 20:44:53 +00:00
my $ rescanactive = 0 ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
foreach ( @ data1 ) {
if ( $ _ =~ /^(lastscan:)([0-9]*)/ ) {
# we found the lastscan entry
2017-05-28 19:22:04 +00:00
my ( $ sec , $ min , $ hour , $ mday , $ mon , $ year , $ wday , $ yday , $ isdst ) =
2014-07-06 22:13:50 +00:00
localtime ( $ 2 ) ;
$ year = $ year + 1900 ;
2015-11-07 19:17:27 +00:00
readingsBulkUpdate ( $ hash , "scan_last" , "$mday-" . ( $ mon + 1 ) . "-$year " . # CD 0016 Monat korrigiert
2014-07-06 22:13:50 +00:00
"$hour:$min:$sec" ) ;
2017-06-22 20:44:53 +00:00
#readingsBulkUpdate( $hash, "scanlast", strftime("%Y-%m-%d %H:%M:%S", localtime($2))); # CD 0040
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(scanning:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "scanning" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(rescan:)([0-9]*)/ ) {
if ( $ 2 eq "1" ) {
2017-06-22 20:44:53 +00:00
readingsBulkUpdate ( $ hash , "scanning" , "yes" ) ;
$ rescanactive = 1 ;
$ hash - > { helper } { scanstart } = time ( ) unless defined ( $ hash - > { helper } { scanstart } ) ; # CD 0040
2014-07-06 22:13:50 +00:00
} else {
2017-06-22 20:44:53 +00:00
readingsBulkUpdate ( $ hash , "scanning" , "no" ) ;
# CD 0040 start
if ( defined $ hash - > { helper } { scanstart } ) {
readingsBulkUpdate ( $ hash , "scanduration" , int ( time ( ) - $ hash - > { helper } { scanstart } ) ) ;
delete $ hash - > { helper } { scanstart } ;
}
# CD 0040 end
2014-07-06 22:13:50 +00:00
}
next ;
} elsif ( $ _ =~ /^(version:)([0-9\.]*)/ ) {
readingsBulkUpdate ( $ hash , "serverversion" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(playercount:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "players" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(snplayercount:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "players_mysb" , $ 2 ) ;
$ currentplayerid = "none" ;
$ addplayers = false ;
next ;
} elsif ( $ _ =~ /^(otherplayercount:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "players_other" , $ 2 ) ;
$ currentplayerid = "none" ;
$ addplayers = false ;
next ;
} elsif ( $ _ =~ /^(infototalalbums:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "db_albums" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(infototalartists:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "db_artists" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(infototalsongs:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "db_songs" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(infototalgenres:)([0-9]*)/ ) {
readingsBulkUpdate ( $ hash , "db_genres" , $ 2 ) ;
next ;
} elsif ( $ _ =~ /^(playerid:)($dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd[:|-]$dd)/ ) {
my $ id = join ( "" , split ( ":" , $ 2 ) ) ;
2016-10-01 16:43:31 +00:00
if ( $ addplayers == true ) { # CD 0017 fixed ==
2014-07-06 22:13:50 +00:00
$ players { $ id } { ID } = $ id ;
$ players { $ id } { MAC } = $ 2 ;
$ currentplayerid = $ id ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
2018-04-22 19:53:00 +00:00
# CD 0044 auf IP-Adresse prüfen
} elsif ( $ _ =~ /^(playerid:)(.*)/ ) {
if ( SB_PLAYER_IsValidIPV4 ( $ 2 ) == 1 ) {
if ( $ addplayers == true ) {
$ players { $ 2 } { ID } = $ 2 ;
$ players { $ 2 } { MAC } = $ 2 ;
$ players { $ 2 } { isplayer } = "1" ; # für virtuellen Player der angelegt wird wenn stream.mp3 abgerufen wird
$ currentplayerid = $ 2 ;
}
}
$ nameactive = 0 ;
next ;
# CD 0044 end
2014-07-06 22:13:50 +00:00
} elsif ( $ _ =~ /^(name:)(.*)/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { name } = $ 2 ;
2017-05-28 19:22:04 +00:00
$ nameactive = 1 ; # CD 0030
2014-07-06 22:13:50 +00:00
}
next ;
} elsif ( $ _ =~ /^(displaytype:)(.*)/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { displaytype } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(model:)(.*)/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { model } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(power:)([0|1])/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { power } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(canpoweroff:)([0|1])/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { canpoweroff } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(connected:)([0|1])/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { connected } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(isplayer:)([0|1])/ ) {
if ( $ currentplayerid ne "none" ) {
2018-04-22 19:53:00 +00:00
$ players { $ currentplayerid } { isplayer } = $ 2 unless defined ( $ players { $ currentplayerid } { isplayer } ) ; # CD 0044 Hack für Zugriff über stream.mp3
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(ip:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{3,5})/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { IP } = $ 2 ;
}
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
} elsif ( $ _ =~ /^(seq_no:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2014-07-06 22:13:50 +00:00
next ;
2016-10-01 16:43:31 +00:00
# CD 0017 start
} elsif ( $ _ =~ /^(isplaying:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2016-10-01 16:43:31 +00:00
next ;
} elsif ( $ _ =~ /^(snplayercount:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2016-10-01 16:43:31 +00:00
next ;
} elsif ( $ _ =~ /^(otherplayercount:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2016-10-01 16:43:31 +00:00
next ;
} elsif ( $ _ =~ /^(server:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2016-10-01 16:43:31 +00:00
next ;
} elsif ( $ _ =~ /^(serverurl:)(.*)/ ) {
# just to take care of the keyword
2017-05-28 19:22:04 +00:00
$ nameactive = 0 ; # CD 0030
2016-10-01 16:43:31 +00:00
next ;
# CD 0017 end
2017-05-28 19:22:04 +00:00
# CD 0030 firmware und modelname
} elsif ( $ _ =~ /^(modelname:)(.*)/ ) {
if ( $ currentplayerid ne "none" ) {
$ players { $ currentplayerid } { model } = $ 2 ;
}
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /^(firmware:)(.*)/ ) {
# just to take care of the keyword
$ nameactive = 0 ;
next ;
} elsif ( $ _ =~ /:/ ) {
$ nameactive = 0 ;
# CD 0030 Ende
2014-07-06 22:13:50 +00:00
} else {
# no keyword found, so let us assume it is part of the player name
2017-05-28 19:22:04 +00:00
# CD 0030 aber nur wenn 'name' noch aktiv ist
if ( ( $ currentplayerid ne "none" ) && ( $ nameactive == 1 ) ) {
2014-07-06 22:13:50 +00:00
$ players { $ currentplayerid } { name } . = $ _ ;
}
}
}
2017-06-22 20:44:53 +00:00
if ( $ rescanactive == 0 ) {
# set default values for stuff not always send
readingsBulkUpdate ( $ hash , "scanning" , "no" ) ;
readingsBulkUpdate ( $ hash , "scandb" , "?" ) ;
readingsBulkUpdate ( $ hash , "scanprogressdone" , "0" ) ;
readingsBulkUpdate ( $ hash , "scanprogresstotal" , "0" ) ;
readingsBulkUpdate ( $ hash , "scanlastfailed" , "none" ) ;
}
2014-07-06 22:13:50 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
2016-10-01 16:43:31 +00:00
my @ ignoredIPs = split ( ',' , AttrVal ( $ name , 'ignoredIPs' , '' ) ) ; # CD 0017
my @ ignoredMACs = split ( ',' , AttrVal ( $ name , 'ignoredMACs' , '' ) ) ; # CD 0017
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
foreach my $ player ( keys % players ) {
2017-05-28 19:22:04 +00:00
my $ playerdata ; # CD 0029
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { isplayer } ) ) {
if ( $ players { $ player } { isplayer } eq "0" ) {
Log3 ( $ hash , 1 , "not a player" ) ;
2016-10-01 16:43:31 +00:00
next ;
2017-05-28 19:22:04 +00:00
}
2016-10-01 16:43:31 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0017 check ignored IPs
if ( defined ( $ players { $ player } { IP } ) ) {
my @ ip = split ( ':' , $ players { $ player } { IP } ) ;
if ( $ ip [ 0 ] ~ ~ @ ignoredIPs ) {
$ players { $ player } { ignore } = 1 ;
next ;
}
}
# CD 0017 check ignored MACs
if ( defined ( $ players { $ player } { MAC } ) ) {
if ( $ players { $ player } { MAC } ~ ~ @ ignoredMACs ) {
$ players { $ player } { ignore } = 1 ;
next ;
}
}
# if the player is not yet known, it will be created
if ( defined ( $ players { $ player } { ID } ) ) {
Dispatch ( $ hash , "SB_PLAYER:$players{$player}{ID}:NONE" , undef ) ;
} else {
Log3 ( $ hash , 1 , "not defined" ) ;
2016-10-01 16:43:31 +00:00
next ;
}
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { name } ) ) {
Dispatch ( $ hash , "SB_PLAYER:$players{$player}{ID}:" .
"name $players{$player}{name}" , undef ) ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
# CD 0029 start
if ( defined ( $ players { $ player } { model } ) ) {
$ playerdata = $ players { $ player } { model } . " " ;
} else {
$ playerdata = "unknown " ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { canpoweroff } ) ) {
$ playerdata . = $ players { $ player } { canpoweroff } . " " ;
} else {
$ playerdata . = "unknown " ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { displaytype } ) ) {
$ playerdata . = $ players { $ player } { displaytype } . " " ;
} else {
$ playerdata . = "unknown " ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { connected } ) ) {
$ playerdata . = $ players { $ player } { connected } . " " ;
} else {
$ playerdata . = "unknown" ;
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { IP } ) ) {
$ playerdata . = $ players { $ player } { IP } ;
} else {
$ playerdata . = "unknown" ;
}
Dispatch ( $ hash , "SB_PLAYER:$players{$player}{ID}:" .
"playerdata $playerdata" , undef ) ;
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
# CD 0029 end
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
if ( defined ( $ players { $ player } { power } ) ) {
Dispatch ( $ hash , "SB_PLAYER:$players{$player}{ID}:" .
"power $players{$player}{power}" , undef ) ;
}
2014-07-06 22:13:50 +00:00
}
# the list for the sync masters
# make all client create e new sync master list
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SYNCMASTER" ,
"FLUSH all" , undef ) ;
2014-07-06 22:13:50 +00:00
# now send the list for the sync masters
2017-05-28 19:22:04 +00:00
@ SB_SERVER_SM = ( ) ;
2014-07-06 22:13:50 +00:00
foreach my $ player ( keys % players ) {
2016-10-01 16:43:31 +00:00
next if defined ( $ players { $ player } { ignore } ) ;
my $ uniqueid = join ( "" , split ( ":" , $ players { $ player } { MAC } ) ) ;
Log3 ( $ hash , 1 , "SB_SERVER_ParseServerStatus($name): player has no name" ) unless defined ( $ players { $ player } { name } ) ;
Log3 ( $ hash , 1 , "SB_SERVER_ParseServerStatus($name): player has no MAC" ) unless defined ( $ players { $ player } { MAC } ) ;
2017-05-28 19:22:04 +00:00
push @ SB_SERVER_SM , "ADD $players{$player}{name} " .
"$players{$player}{MAC} $uniqueid" # CD 0029 aufteilen wenn Hardware zu schwach
}
2018-04-22 19:53:00 +00:00
push @ SB_SERVER_SM , 'DONE' ; # CD 0045
2017-05-28 19:22:04 +00:00
# CD 0029 start
if ( scalar ( @ SB_SERVER_SM ) > 0 ) {
RemoveInternalTimer ( "SB_SERVER_tcb_SendSyncMasters:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendSyncMasters" ,
"SB_SERVER_tcb_SendSyncMasters:$name" ,
0 ) ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0029 end
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
SB_SERVER_BuildPlayerList ( $ hash ) ; # CD 0024
2014-07-06 22:13:50 +00:00
return ;
}
2017-05-28 19:22:04 +00:00
# CD 0029 start
2018-04-22 19:53:00 +00:00
# für schwache Hardware Übertragung aufteilen
2017-05-28 19:22:04 +00:00
sub SB_SERVER_tcb_SendSyncMasters ( $ ) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendSyncMasters:$name" ) ;
my $ a ;
my $ t = time ( ) ;
do {
2018-04-22 19:53:00 +00:00
$ a = shift @ SB_SERVER_SM ; # CD 0045 Reihenfolge beibehalten
2017-05-28 19:22:04 +00:00
if ( defined ( $ a ) ) {
SB_SERVER_Broadcast ( $ hash , "SYNCMASTER" , $ a , undef ) ;
}
} while ( ( time ( ) < $ t + 0.05 ) && defined ( $ a ) ) ;
if ( scalar ( @ SB_SERVER_SM ) > 0 ) {
2018-04-22 19:53:00 +00:00
#Log 0,"SB_SERVER_tcb_SendSyncMasters: ".scalar(@SB_SERVER_SM)." entries remaining";
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) + 0.05 ,
"SB_SERVER_tcb_SendSyncMasters" ,
"SB_SERVER_tcb_SendSyncMasters:$name" ,
0 ) ;
}
}
# CD 0029 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# Parse the return values of the favorites items
# ----------------------------------------------------------------------------
sub SB_SERVER_FavoritesParse ( $$ ) {
my ( $ hash , $ str ) = @ _ ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my $ name = $ hash - > { NAME } ;
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_FavoritesParse($name): called" ) ;
2014-07-06 22:13:50 +00:00
# flush the existing list
foreach my $ titi ( keys % { $ favorites { $ name } } ) {
delete ( $ favorites { $ name } { $ titi } ) ;
}
# split up the string we got
my @ data = split ( " " , $ str ) ;
# eliminate the first entries of the response
# some more comment
# typically 'items'
if ( $ data [ 0 ] =~ /^(items)*/ ) {
my $ notneeded = shift ( @ data ) ;
2017-05-28 19:22:04 +00:00
}
2014-07-06 22:13:50 +00:00
# typically the start index being a number
if ( $ data [ 0 ] =~ /^([0-9])*/ ) {
my $ notneeded = shift ( @ data ) ;
}
# typically the start index being a number
my $ maxwanted = 100 ;
if ( $ data [ 0 ] =~ /^([0-9])*/ ) {
$ maxwanted = int ( shift ( @ data ) ) ;
}
2017-05-28 19:22:04 +00:00
# find the maximum number of favorites. That is typically at the
2014-07-06 22:13:50 +00:00
# end of the server response. So check there first
my $ totals = 0 ;
my $ lastdata = $ data [ $# data ] ;
if ( $ lastdata =~ /^(count:)([0-9]*)/ ) {
$ totals = $ 2 ;
# remove the last element from the array
pop ( @ data ) ;
} else {
my $ i = 0 ;
my $ delneeded = false ;
foreach ( @ data ) {
if ( $ _ =~ /^(count:)([0-9]*)/ ) {
$ totals = $ 2 ;
$ delneeded = true ;
last ;
} else {
$ i + + ;
}
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# delete the element from the list
if ( $ delneeded == true ) {
splice ( @ data , $ i , 1 ) ;
}
}
}
readingsSingleUpdate ( $ hash , "favoritestotal" , $ totals , 0 ) ;
my $ favname = "" ;
if ( $ data [ 0 ] =~ /^(title:)(.*)/ ) {
$ favname = $ 2 ;
shift ( @ data ) ;
}
readingsSingleUpdate ( $ hash , "favoritesname" , $ favname , 0 ) ;
# check if we got all the favoites with our response
if ( $ totals > $ maxwanted ) {
# we asked for too less data, there are more favorites defined
}
# treat the rest of the string
my $ namestarted = false ;
my $ firstone = true ;
my $ namebuf = "" ;
my $ idbuf = "" ;
my $ hasitemsbuf = false ;
my $ isaudiobuf = "" ;
2016-10-01 16:43:31 +00:00
my $ isplaylist = false ;
2018-04-22 19:53:00 +00:00
my $ url = "?" ; # CD 0009 hinzugefügt
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
my $ cnt = 0 ;
2014-07-06 22:13:50 +00:00
foreach ( @ data ) {
2016-10-01 16:43:31 +00:00
#Log 0,$_;
2014-07-06 22:13:50 +00:00
if ( $ _ =~ /^(id:|ID:)([A-Za-z0-9\.]*)/ ) {
# we found an ID, that is typically the start of a new session
# so save the old session first
if ( $ firstone == false ) {
2016-10-01 16:43:31 +00:00
if ( ( $ hasitemsbuf == false ) || ( $ isplaylist == true ) ) {
# derive our hash entry
2017-05-28 19:22:04 +00:00
$ namebuf = "noname_" . $ cnt + + if ( $ namebuf =~ /^\s*$/ ) ; # CD 0037
2018-04-22 19:53:00 +00:00
my $ entryuid = SB_SERVER_FavoritesName2UID ( $ namebuf ) ; # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
2016-10-01 16:43:31 +00:00
$ favorites { $ name } { $ entryuid } = {
ID = > $ idbuf ,
Name = > $ namebuf ,
2018-04-22 19:53:00 +00:00
URL = > $ url , } ; # CD 0009 hinzugefügt
2016-10-01 16:43:31 +00:00
$ namebuf = "" ;
$ isaudiobuf = "" ;
2018-04-22 19:53:00 +00:00
$ url = "?" ; # CD 0009 hinzugefügt
2016-10-01 16:43:31 +00:00
$ hasitemsbuf = false ;
$ isplaylist = false ;
} else {
# that is a folder we found, but we don't handle that
2017-05-28 19:22:04 +00:00
}
2014-07-06 22:13:50 +00:00
}
$ firstone = false ;
$ idbuf = $ 2 ;
# if there has been a name found before, end it now
if ( $ namestarted == true ) {
$ namestarted = false ;
}
} elsif ( $ _ =~ /^(isaudio:)([0|1]?)/ ) {
$ isaudiobuf = $ 2 ;
if ( $ namestarted == true ) {
$ namestarted = false ;
}
} elsif ( $ _ =~ /^(hasitems:)([0|1]?)/ ) {
2017-05-28 19:22:04 +00:00
if ( int ( $ 2 ) == 0 ) {
2014-07-06 22:13:50 +00:00
$ hasitemsbuf = false ;
} else {
$ hasitemsbuf = true ;
}
if ( $ namestarted == true ) {
$ namestarted = false ;
}
2016-10-01 16:43:31 +00:00
# CD 0018 start
} elsif ( $ _ =~ /^(type:)(.*)/ ) {
$ isplaylist = true if ( $ 2 eq "playlist" ) ;
2014-07-06 22:13:50 +00:00
if ( $ namestarted == true ) {
2016-10-01 16:43:31 +00:00
$ namestarted = false ;
2014-07-06 22:13:50 +00:00
}
2016-10-01 16:43:31 +00:00
# CD 0018 end
2015-03-19 20:32:45 +00:00
#} elsif( $_ =~ /^(name:)([0-9a-zA-Z]*)/ ) { # CD 0007 # CD 0009 deaktiviert
2018-04-22 19:53:00 +00:00
} elsif ( $ _ =~ /^(name:)(.*)/ ) { # CD 0009 hinzugefügt
2014-07-06 22:13:50 +00:00
$ namebuf = $ 2 ;
$ namestarted = true ;
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
# CD 0009 start
} elsif ( $ _ =~ /^(url:)(.*)/ ) {
$ url = $ 2 ;
$ url =~ s/file:\/\/\/// ;
# CD 0009 end
} else {
2014-07-06 22:13:50 +00:00
# no regexp matched, so it must be part of the name
if ( $ namestarted == true ) {
$ namebuf . = " " . $ _ ;
}
}
}
# capture the last element also
if ( ( $ namebuf ne "" ) && ( $ idbuf ne "" ) ) {
2016-10-01 16:43:31 +00:00
if ( ( $ hasitemsbuf == false ) || ( $ isplaylist == true ) ) {
2015-03-19 20:32:45 +00:00
# CD 0003 replaced ** my $entryuid = join( "", split( " ", $namebuf ) ); ** with:
2017-05-28 19:22:04 +00:00
$ namebuf = "noname_" . $ cnt + + if ( $ namebuf =~ /^\s*$/ ) ; # CD 0037
2018-04-22 19:53:00 +00:00
my $ entryuid = SB_SERVER_FavoritesName2UID ( $ namebuf ) ; # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
2014-07-06 22:13:50 +00:00
$ favorites { $ name } { $ entryuid } = {
ID = > $ idbuf ,
2015-03-19 20:32:45 +00:00
Name = > $ namebuf ,
2018-04-22 19:53:00 +00:00
URL = > $ url , } ; # CD 0009 hinzugefügt
2014-07-06 22:13:50 +00:00
} else {
# that is a folder we found, but we don't handle that
}
}
# make all client create e new favorites list
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "FAVORITES" ,
"FLUSH all" , undef ) ;
2014-07-06 22:13:50 +00:00
# find all the names and broadcast to our clients
$ favsetstring = "favorites:" ;
2017-05-28 19:22:04 +00:00
@ SB_SERVER_FAVS = ( ) ; # CD 0029
2014-07-06 22:13:50 +00:00
foreach my $ titi ( keys % { $ favorites { $ name } } ) {
2017-05-28 19:22:04 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_ParseFavorites($name): " .
"ID:" . $ favorites { $ name } { $ titi } { ID } .
" Name:" . $ favorites { $ name } { $ titi } { Name } . " $titi" ) ;
$ favsetstring . = "$titi," ;
push @ SB_SERVER_FAVS , "ADD $name $favorites{$name}{$titi}{ID} " .
"$titi $favorites{$name}{$titi}{URL} LMS $favorites{$name}{$titi}{Name}" ; # CD 0009 URL an Player schicken # CD 0029 aufteilen wenn Hardware zu schwach
}
# CD 0029 start
if ( scalar ( @ SB_SERVER_FAVS ) > 0 ) {
RemoveInternalTimer ( "SB_SERVER_tcb_SendFavorites:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendFavorites" ,
"SB_SERVER_tcb_SendFavorites:$name" ,
0 ) ;
2014-07-06 22:13:50 +00:00
}
2017-05-28 19:22:04 +00:00
# CD 0029 end
2014-07-06 22:13:50 +00:00
#chop( $favsetstring );
#$favsetstring .= " ";
}
2017-05-28 19:22:04 +00:00
# CD 0029 start
2018-04-22 19:53:00 +00:00
# für schwache Hardware Übertragung aufteilen
2017-05-28 19:22:04 +00:00
sub SB_SERVER_tcb_SendFavorites ( $ ) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendFavorites:$name" ) ;
my $ a ;
my $ t = time ( ) ;
do {
2018-04-22 19:53:00 +00:00
$ a = shift @ SB_SERVER_FAVS ; # CD 0045 Reihenfolge beibehalten
2017-05-28 19:22:04 +00:00
if ( defined ( $ a ) ) {
SB_SERVER_Broadcast ( $ hash , "FAVORITES" , $ a , undef ) ; # CD 0009 URL an Player schicken
}
} while ( ( time ( ) < $ t + 0.05 ) && defined ( $ a ) ) ;
if ( scalar ( @ SB_SERVER_FAVS ) > 0 ) {
#Log 0,"SB_SERVER_tcb_SendFavorites: ".scalar(@SB_SERVER_FAVS)." entries remaining";
InternalTimer ( gettimeofday ( ) + 0.05 ,
"SB_SERVER_tcb_SendFavorites" ,
"SB_SERVER_tcb_SendFavorites:$name" ,
0 ) ;
}
}
# CD 0029 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# generate a UID for the hash entry from the name
# ----------------------------------------------------------------------------
sub SB_SERVER_FavoritesName2UID ( $ ) {
my $ namestr = shift ( @ _ ) ;
# eliminate spaces
2018-04-22 19:53:00 +00:00
$ namestr = join ( "_" , split ( " " , $ namestr ) ) ; # CD 0009 Leerzeichen durch _ ersetzen statt löschen
2015-03-19 20:32:45 +00:00
2018-04-22 19:53:00 +00:00
# CD 0009 verschiedene Sonderzeichen ersetzen und nicht mehr löschen
my % Sonderzeichen = ( "ä" = > "ae" , "Ä" = > "Ae" , "ü" = > "ue" , "Ü" = > "Ue" , "ö" = > "oe" , "Ö" = > "Oe" , "ß" = > "ss" ,
"é" = > "e" , "è" = > "e" , "ë" = > "e" , "à" = > "a" , "ç" = > "c" ) ;
2015-03-19 20:32:45 +00:00
my $ Sonderzeichenkeys = join ( "|" , keys ( % Sonderzeichen ) ) ;
$ namestr =~ s/($Sonderzeichenkeys)/$Sonderzeichen{$1}/g ;
2017-05-28 19:22:04 +00:00
# $namestr =~ s/($Sonderzeichenkeys)/$Sonderzeichen{$1}||''/g;
2015-03-19 20:32:45 +00:00
# CD 0009
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
# CD 0034 start
my $ rc = eval
{
require Text::Unaccent ;
$ namestr = Text::Unaccent:: unac_string ( 'UTF8' , $ namestr ) ;
} ;
# CD 0034 end
2014-07-06 22:13:50 +00:00
# this defines the regexp. Please add new stuff with the seperator |
2018-04-22 19:53:00 +00:00
# CD 0003 changed öÜ to ö|Ü
my $ tobereplaced = '[Ä|ä|Ö|ö|Ü|ü|\[|\]|\{|\}|\(|\)|\\\\|,|:|\?|;|' . # CD 0011 ,:? hinzugefügt # CD 0035 ; hinzugefügt
'\/|\'|\.|\"|\^|°|\$|\||%|@|*|#|&|\+]' ; # CD 0009 + hinzugefügt # CD 0070 * und # hinzugefügt
2014-07-06 22:13:50 +00:00
$ namestr =~ s/$tobereplaced//g ;
return ( $ namestr ) ;
}
# ----------------------------------------------------------------------------
# push a command to the buffer
# ----------------------------------------------------------------------------
sub SB_SERVER_CMDStackPush ( $$ ) {
my ( $ hash , $ cmd ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2014-07-06 22:13:50 +00:00
my $ n = $ SB_SERVER_CmdStack { $ name } { last_n } ;
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
$ n = 0 if ( ! defined ( $ n ) ) ; # CD 0007
2014-07-06 22:13:50 +00:00
if ( $ n > AttrVal ( $ name , "maxcmdstack" , 200 ) ) {
2015-03-19 20:32:45 +00:00
Log3 ( $ hash , 5 , "SB_SERVER_CMDStackPush($name): limit reached" ) ;
SB_SERVER_CMDStackPop ( $ hash ) ; # CD 0007 added
#return; # CD 0007 disabled
2014-07-06 22:13:50 +00:00
}
2015-03-19 20:32:45 +00:00
$ SB_SERVER_CmdStack { $ name } { $ n } { CMD } = $ cmd ;
$ SB_SERVER_CmdStack { $ name } { $ n } { TS } = time ( ) ; # CD 0007
2014-07-06 22:13:50 +00:00
$ n = $ n + 1 ;
$ SB_SERVER_CmdStack { $ name } { last_n } = $ n ;
2015-03-19 20:32:45 +00:00
$ SB_SERVER_CmdStack { $ name } { first_n } = $ n if ( ! defined ( $ SB_SERVER_CmdStack { $ name } { first_n } ) ) ; # CD 0007
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# update overall number of entries
2017-05-28 19:22:04 +00:00
$ SB_SERVER_CmdStack { $ name } { cnt } = $ SB_SERVER_CmdStack { $ name } { last_n } -
2014-07-06 22:13:50 +00:00
$ SB_SERVER_CmdStack { $ name } { first_n } + 1 ;
2015-03-19 20:32:45 +00:00
$ hash - > { CMDSTACK } = $ SB_SERVER_CmdStack { $ name } { cnt } ; # CD 0007
2014-07-06 22:13:50 +00:00
}
# ----------------------------------------------------------------------------
# pop a command from the buffer
# ----------------------------------------------------------------------------
sub SB_SERVER_CMDStackPop ( $ ) {
my ( $ hash ) = @ _ ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my $ name = $ hash - > { NAME } ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my $ n = $ SB_SERVER_CmdStack { $ name } { first_n } ;
2015-03-19 20:32:45 +00:00
$ n = 0 if ( ! defined ( $ n ) ) ; # CD 0007
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my $ res = "" ;
# return the first element of the list
if ( defined ( $ SB_SERVER_CmdStack { $ name } { $ n } ) ) {
2015-03-19 20:32:45 +00:00
$ res = $ SB_SERVER_CmdStack { $ name } { $ n } { CMD } ;
$ res = "empty" if ( $ SB_SERVER_CmdStack { $ name } { $ n } { TS } < time ( ) - 300 ) ; # CD 0007 drop commands older than 5 minutes
2014-07-06 22:13:50 +00:00
} else {
2015-03-19 20:32:45 +00:00
$ res = "empty" ;
2014-07-06 22:13:50 +00:00
}
# and now remove the first element
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
delete ( $ SB_SERVER_CmdStack { $ name } { $ n } ) ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
$ n = $ n + 1 ;
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
if ( $ n <= $ SB_SERVER_CmdStack { $ name } { last_n } ) { # CD 0000 changed first_n to last_n
2014-07-06 22:13:50 +00:00
$ SB_SERVER_CmdStack { $ name } { first_n } = $ n ;
# update overall number of entries
2017-05-28 19:22:04 +00:00
$ SB_SERVER_CmdStack { $ name } { cnt } = $ SB_SERVER_CmdStack { $ name } { last_n } -
2014-07-06 22:13:50 +00:00
$ SB_SERVER_CmdStack { $ name } { first_n } + 1 ;
} else {
# end of list reached
$ SB_SERVER_CmdStack { $ name } { last_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { first_n } = 0 ;
$ SB_SERVER_CmdStack { $ name } { cnt } = 0 ;
}
2015-03-19 20:32:45 +00:00
$ hash - > { CMDSTACK } = $ SB_SERVER_CmdStack { $ name } { cnt } ; # CD 0007
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
return ( $ res ) ;
}
2015-03-19 20:32:45 +00:00
# CD 0011 start
# ----------------------------------------------------------------------------
# parse the list of known alarm playlists
# ----------------------------------------------------------------------------
sub SB_SERVER_ParseServerAlarmPlaylists ( $$ ) {
my ( $ hash , $ dataptr ) = @ _ ;
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
my $ name = $ hash - > { NAME } ;
Log3 ( $ hash , 4 , "SB_SERVER_ParseServerAlarmPlaylists($name): called" ) ;
# force all clients to delete alarm playlists
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "ALARMPLAYLISTS" ,
"FLUSH all" , undef ) ;
@ SB_SERVER_AL_PLS = split ( "category:" , join ( " " , @ { $ dataptr } ) ) ;
2018-04-22 19:53:00 +00:00
# CD 0029 Übertragung an Player aufteilen
2017-05-28 19:22:04 +00:00
if ( scalar ( @ SB_SERVER_AL_PLS ) > 0 ) {
RemoveInternalTimer ( "SB_SERVER_tcb_SendAlarmPlaylists:$name" ) ;
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendAlarmPlaylists" ,
"SB_SERVER_tcb_SendAlarmPlaylists:$name" ,
0 ) ;
2015-03-19 20:32:45 +00:00
}
}
# CD 0011 end
2017-05-28 19:22:04 +00:00
# CD 0029 start
2018-04-22 19:53:00 +00:00
# für schwache Hardware Übertragung aufteilen
2017-05-28 19:22:04 +00:00
sub SB_SERVER_tcb_SendAlarmPlaylists ( $ ) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendAlarmPlaylists:$name" ) ;
my $ a ;
my $ t = time ( ) ;
do {
2018-04-22 19:53:00 +00:00
$ a = shift @ SB_SERVER_AL_PLS ; # CD 0045 Reihenfolge beibehalten
2017-05-28 19:22:04 +00:00
if ( defined ( $ a ) ) {
my $ i1 = index ( $ a , " title:" ) ;
my $ i2 = index ( $ a , " url:" ) ;
my $ i3 = index ( $ a , " singleton:" ) ;
if ( ( $ i1 != - 1 ) && ( $ i2 != - 1 ) && ( $ i3 != - 1 ) ) {
my $ url = substr ( $ a , $ i2 + 5 , $ i3 - $ i2 - 5 ) ;
$ url = substr ( $ a , $ i1 + 7 , $ i2 - $ i1 - 7 ) if ( $ url eq "" ) ;
my $ pn = SB_SERVER_FavoritesName2UID ( decode ( 'utf-8' , $ url ) ) ;
SB_SERVER_Broadcast ( $ hash , "ALARMPLAYLISTS" ,
"ADD $pn category " . substr ( $ a , 0 , $ i1 ) , undef ) ;
SB_SERVER_Broadcast ( $ hash , "ALARMPLAYLISTS" ,
"ADD $pn title " . substr ( $ a , $ i1 + 7 , $ i2 - $ i1 - 7 ) , undef ) ;
SB_SERVER_Broadcast ( $ hash , "ALARMPLAYLISTS" ,
"ADD $pn url $url" , undef ) ;
}
}
} while ( ( time ( ) < $ t + 0.05 ) && defined ( $ a ) ) ;
if ( scalar ( @ SB_SERVER_AL_PLS ) > 0 ) {
#Log 0,"SB_SERVER_tcb_SendAlarmPlaylists: ".scalar(@SB_SERVER_AL_PLS)." entries remaining";
InternalTimer ( gettimeofday ( ) + 0.05 ,
"SB_SERVER_tcb_SendAlarmPlaylists" ,
"SB_SERVER_tcb_SendAlarmPlaylists:$name" ,
0 ) ;
}
}
# CD 0029 end
2014-07-06 22:13:50 +00:00
# ----------------------------------------------------------------------------
# parse the list of known Playlists
# ----------------------------------------------------------------------------
sub SB_SERVER_ParseServerPlaylists ( $$ ) {
my ( $ hash , $ dataptr ) = @ _ ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
my $ name = $ hash - > { NAME } ;
2014-07-23 09:01:39 +00:00
Log3 ( $ hash , 4 , "SB_SERVER_ParseServerPlaylists($name): called" ) ;
2014-07-06 22:13:50 +00:00
my $ namebuf = "" ;
my $ uniquename = "" ;
my $ idbuf = - 1 ;
2017-05-28 19:22:04 +00:00
2014-07-06 22:13:50 +00:00
# typically the start index being a number
if ( $ dataptr - > [ 0 ] =~ /^([0-9])*/ ) {
shift ( @ { $ dataptr } ) ;
} else {
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerPlaylists($name): entry is " .
"not the start number" ) ;
return ;
}
# typically the max index being a number
if ( $ dataptr - > [ 0 ] =~ /^([0-9])*/ ) {
shift ( @ { $ dataptr } ) ;
} else {
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerPlaylists($name): entry is " .
"not the end number" ) ;
return ;
}
my $ datastr = join ( " " , @ { $ dataptr } ) ;
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerPlaylists($name): data to parse: " .
$ datastr ) ;
2015-03-19 20:32:45 +00:00
# make all client create a new favorites list
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "PLAYLISTS" ,
"FLUSH all" , undef ) ;
2014-07-06 22:13:50 +00:00
my @ data1 = split ( " " , $ datastr ) ;
2017-05-28 19:22:04 +00:00
@ SB_SERVER_PLS = ( ) ;
my $ cnt = 1 ; # CD 0037
2014-07-06 22:13:50 +00:00
foreach ( @ data1 ) {
2017-05-28 19:22:04 +00:00
if ( $ _ =~ /^(id:)(.*)/ ) {
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerPlaylists($name): " .
"id:$idbuf name:$namebuf " ) ;
if ( $ idbuf != - 1 ) {
$ namebuf = "noname_" . $ cnt + + if ( $ namebuf =~ /^\s*$/ ) ; # CD 0037
2018-04-22 19:53:00 +00:00
$ uniquename = SB_SERVER_FavoritesName2UID ( $ namebuf ) ; # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
2017-05-28 19:22:04 +00:00
push @ SB_SERVER_PLS , "ADD $namebuf $idbuf $uniquename LMS" ; # CD 0029
}
$ idbuf = $ 2 ;
$ namebuf = "" ;
$ uniquename = "" ;
next ;
} elsif ( $ _ =~ /^(playlist:)(.*)/ ) {
$ namebuf = $ 2 ;
next ;
} elsif ( $ _ =~ /^(count:)([0-9]*)/ ) {
# the last entry of the return
Log3 ( $ hash , 5 , "SB_SERVER_ParseServerPlaylists($name): " .
"id:$idbuf name:$namebuf " ) ;
if ( $ idbuf != - 1 ) {
$ namebuf = "noname_" . $ cnt + + if ( $ namebuf =~ /^\s*$/ ) ; # CD 0037
2018-04-22 19:53:00 +00:00
$ uniquename = SB_SERVER_FavoritesName2UID ( $ namebuf ) ; # CD 0009 decode hinzugefügt # CD 0010 decode wieder entfernt
2017-05-28 19:22:04 +00:00
push @ SB_SERVER_PLS , "ADD $namebuf $idbuf $uniquename LMS" ; # CD 0029
}
2014-07-06 22:13:50 +00:00
2017-05-28 19:22:04 +00:00
} else {
$ namebuf . = "_" . $ _ ;
next ;
}
}
# CD 0029 start
if ( scalar ( @ SB_SERVER_PLS ) > 0 ) {
InternalTimer ( gettimeofday ( ) + 0.01 ,
"SB_SERVER_tcb_SendPlaylists" ,
"SB_SERVER_tcb_SendPlaylists:$name" ,
0 ) ;
}
# CD 0029 end
2014-07-06 22:13:50 +00:00
return ;
}
2017-05-28 19:22:04 +00:00
# CD 0029 start
2018-04-22 19:53:00 +00:00
# für schwache Hardware Übertragung aufteilen
2017-05-28 19:22:04 +00:00
sub SB_SERVER_tcb_SendPlaylists ( $ ) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendPlaylists:$name" ) ;
my $ a ;
my $ t = time ( ) ;
do {
2018-04-22 19:53:00 +00:00
$ a = shift @ SB_SERVER_PLS ; # CD 0045 Reihenfolge beibehalten
2017-05-28 19:22:04 +00:00
if ( defined ( $ a ) ) {
SB_SERVER_Broadcast ( $ hash , "PLAYLISTS" , $ a , undef ) ;
}
} while ( ( time ( ) < $ t + 0.05 ) && defined ( $ a ) ) ;
if ( scalar ( @ SB_SERVER_PLS ) > 0 ) {
#Log 0,"SB_SERVER_tcb_SendPlaylists: ".scalar(@SB_SERVER_PLS)." entries remaining";
InternalTimer ( gettimeofday ( ) + 0.05 ,
"SB_SERVER_tcb_SendPlaylists" ,
"SB_SERVER_tcb_SendPlaylists:$name" ,
0 ) ;
}
}
# CD 0029 end
2015-03-19 20:32:45 +00:00
# CD 0008 start
sub SB_SERVER_CheckConnection ($) {
my ( $ in ) = shift ;
my ( undef , $ name ) = split ( ':' , $ in ) ;
my $ hash = $ defs { $ name } ;
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2017-06-04 10:03:33 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_CheckConnection($name): STATE: " . ReadingsVal ( $ name , "state" , "unknown" ) . " power: " . ReadingsVal ( $ name , "power" , "X" ) ) ; # CD 0009 level 2->3 # CD 0038 state statt STATE verwenden
2015-03-19 20:32:45 +00:00
if ( ReadingsVal ( $ name , "power" , "X" ) ne "on" ) {
Log3 ( $ hash , 3 , "SB_SERVER_CheckConnection($name): forcing power on" ) ; # CD 0009 level 2->3
2017-05-28 19:22:04 +00:00
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { pingCounter } = 0 ;
2017-05-28 19:22:04 +00:00
SB_SERVER_Broadcast ( $ hash , "SERVER" ,
2015-03-19 20:32:45 +00:00
"IP " . $ hash - > { IP } . ":" .
2018-04-22 19:53:00 +00:00
$ hash - > { helper } { httpport } ) ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { doBroadcast } = 1 ;
SB_SERVER_LMS_Status ( $ hash ) ;
if ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "false" ) {
readingsSingleUpdate ( $ hash , "power" , "on" , 1 ) ;
} elsif ( AttrVal ( $ name , "doalivecheck" , "false" ) eq "true" ) {
# start the alive checking mechanism
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) +
2015-03-19 20:32:45 +00:00
AttrVal ( $ name , "alivetimer" , 10 ) ,
2016-10-01 16:43:31 +00:00
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2015-03-19 20:32:45 +00:00
0 ) ;
}
}
RemoveInternalTimer ( "CheckConnection:$name" ) ;
2017-05-28 19:22:04 +00:00
}
2015-03-19 20:32:45 +00:00
# CD 0008 end
2014-07-06 22:13:50 +00:00
2014-07-23 09:01:39 +00:00
# ----------------------------------------------------------------------------
# the Notify function
# ----------------------------------------------------------------------------
sub SB_SERVER_Notify ( $$ ) {
2015-03-19 20:32:45 +00:00
my ( $ hash , $ dev_hash ) = @ _ ;
my $ name = $ hash - > { NAME } ; # own name / hash
my $ devName = $ dev_hash - > { NAME } ; # Device that created the events
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2015-03-19 20:32:45 +00:00
# CD start
if ( $ dev_hash - > { NAME } eq "global" && grep ( m/^INITIALIZED$|^REREADCFG$/ , @ { $ dev_hash - > { CHANGED } } ) ) {
2018-04-22 19:53:00 +00:00
SB_SERVER_TryConnect ( $ hash , 0 ) unless defined ( $ hash - > { helper } { disableReconnect } ) ; # CD 0038
2015-03-19 20:32:45 +00:00
}
# CD end
2017-07-26 19:21:33 +00:00
#Log3( $hash, 3, "SB_SERVER_Notify($name): called" .
# "Own:" . $name . " Device:" . $devName . " Events:" . (join " ",@{$dev_hash->{CHANGED}}) );
2015-03-19 20:32:45 +00:00
2017-05-28 19:22:04 +00:00
# CD 0024 start
if ( grep ( m/^SAVE$|^SHUTDOWN$/ , @ { $ dev_hash - > { CHANGED } } ) ) { # CD 0043 auch bei SHUTDOWN speichern
SB_SERVER_SaveSyncGroups ( $ hash ) if ( $ SB_SERVER_hasDataDumper == 1 ) ;
SB_SERVER_SaveServerStates ( $ hash ) if ( $ SB_SERVER_hasDataDumper == 1 ) ;
}
# CD 0024 end
2015-03-19 20:32:45 +00:00
# CD 0008 start
if ( $ devName eq $ name ) {
if ( grep ( m/^DISCONNECTED$/ , @ { $ dev_hash - > { CHANGED } } ) ) {
2017-06-04 10:03:33 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_Notify($name): DISCONNECTED - STATE: " . ReadingsVal ( $ name , "state" , "unknown" ) . " power: " . ReadingsVal ( $ name , "power" , "X" ) ) ; # CD 0009 level 2->3
2015-03-19 20:32:45 +00:00
RemoveInternalTimer ( "CheckConnection:$name" ) ;
}
if ( grep ( m/^CONNECTED$/ , @ { $ dev_hash - > { CHANGED } } ) ) {
2017-06-04 10:03:33 +00:00
Log3 ( $ hash , 3 , "SB_SERVER_Notify($name): CONNECTED - STATE: " . ReadingsVal ( $ name , "state" , "unknown" ) . " power: " . ReadingsVal ( $ name , "power" , "X" ) ) ; # CD 0009 level 2->3
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) + 2 ,
"SB_SERVER_CheckConnection" ,
2015-03-19 20:32:45 +00:00
"CheckConnection:$name" ,
0 ) ;
}
}
# CD 0008 end
if ( $ devName eq $ hash - > { RCCNAME } ) {
if ( ReadingsVal ( $ hash - > { RCCNAME } , "state" , "off" ) eq "off" ) {
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) + 10 ,
2016-10-01 16:43:31 +00:00
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2016-10-01 16:43:31 +00:00
0 ) ;
2015-03-19 20:32:45 +00:00
# CD 0007 use DevIo_Disconnected instead of DevIo_CloseDev
2017-05-28 19:22:04 +00:00
#DevIo_CloseDev( $hash );
DevIo_Disconnected ( $ hash ) ;
2015-03-19 20:32:45 +00:00
$ hash - > { helper } { pingCounter } = 9999 ; # CD 0007
$ hash - > { CLICONNECTION } = "off" ; # CD 0007
# CD 0005 set state after DevIo_CloseDev
# CD 0006 DevIo_setStates requires v7099 of DevIo.pm, replaced with SB_SERVER_setStates
SB_SERVER_setStates ( $ hash , "disconnected" ) ;
} elsif ( ReadingsVal ( $ hash - > { RCCNAME } , "state" , "off" ) eq "on" ) {
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2015-03-19 20:32:45 +00:00
# do an update of the status, but SB CLI must come up
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) + 20 ,
2016-10-01 16:43:31 +00:00
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2016-10-01 16:43:31 +00:00
0 ) ;
2015-03-19 20:32:45 +00:00
} else {
return ( undef ) ;
}
return ( "" ) ;
# CD 0007 start
} elsif ( $ devName eq $ hash - > { PRESENCENAME } ) {
2018-04-22 19:53:00 +00:00
my $ pp = 0 ;
my $ pa = 0 ;
my $ ps ;
foreach my $ line ( @ { $ dev_hash - > { CHANGED } } ) {
my @ args = split ( ':' , $ line ) ;
my $ ps ;
# Spezialfall 'state'
$ ps = trim ( $ args [ 0 ] ) if ( ( @ args == 1 ) && ( $ hash - > { helper } { presenceReading } eq 'state' ) ) ;
# Reading: Value
$ ps = trim ( $ args [ 1 ] ) if ( ( @ args == 2 ) && ( $ hash - > { helper } { presenceReading } eq trim ( $ args [ 0 ] ) ) ) ;
if ( defined ( $ ps ) ) {
Log3 ( $ hash , 3 , "SB_SERVER_Notify($name): $devName changed to " . $ ps ) ; # CD 0023 loglevel 2->3
$ pp = $ ps eq $ hash - > { helper } { presenceValuePresent } ;
$ pa = $ ps eq $ hash - > { helper } { presenceValueAbsent } ;
2016-10-01 16:43:31 +00:00
}
2018-04-22 19:53:00 +00:00
}
# Serverstatus geändert ?
if ( ( $ pa ) || ( $ pp ) ) {
$ hash - > { helper } { lastPRESENCEstate } = $ ps ;
2016-10-01 16:43:31 +00:00
# CD 0023 end
2017-06-04 10:03:33 +00:00
SB_SERVER_RemoveInternalTimers ( $ hash ) ;
2015-03-19 20:32:45 +00:00
# do an update of the status, but SB CLI must come up
2016-10-01 16:43:31 +00:00
# CD 0020 SB_SERVER_tcb_Alive verwenden
2017-05-28 19:22:04 +00:00
InternalTimer ( gettimeofday ( ) + 10 ,
2016-10-01 16:43:31 +00:00
"SB_SERVER_tcb_Alive" ,
2017-05-28 19:22:04 +00:00
"SB_SERVER_Alive:$name" ,
2016-10-01 16:43:31 +00:00
0 ) ;
2015-03-19 20:32:45 +00:00
return ( "" ) ;
}
2018-04-22 19:53:00 +00:00
return ( undef ) ;
2015-03-19 20:32:45 +00:00
# CD 0007 end
} else {
return ( undef ) ;
}
2014-07-23 09:01:39 +00:00
}
2014-11-09 18:01:21 +00:00
# ----------------------------------------------------------------------------
# start up the LMS server status
# ----------------------------------------------------------------------------
sub SB_SERVER_LMS_Status ( $ ) {
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ; # own name / hash
2018-04-22 19:53:00 +00:00
return if ( IsDisabled ( $ name ) ) ; # CD 0046
2015-03-19 20:32:45 +00:00
# CD 0007 login muss als erstes gesendet werden
$ hash - > { helper } { SB_SERVER_LMS_Status } = time ( ) ;
2017-05-28 19:22:04 +00:00
if ( ( $ hash - > { USERNAME } ne "?" ) &&
2015-03-19 20:32:45 +00:00
( $ hash - > { PASSWORD } ne "?" ) ) {
2017-05-28 19:22:04 +00:00
my ( $ user , $ password ) = SB_SERVER_readPassword ( $ hash ) ; # CD 0031
if ( defined ( $ user ) ) {
DevIo_SimpleWrite ( $ hash , "login " .
$ user . " " .
$ password . "\n" ,
0 ) ;
} else {
Log3 ( $ hash , 3 , "SB_SERVER_LMS_Status($name): login " .
"required but no username and password specified" ) ;
}
2015-03-19 20:32:45 +00:00
}
2017-05-28 19:22:04 +00:00
2014-11-09 18:01:21 +00:00
# subscribe us
DevIo_SimpleWrite ( $ hash , "listen 1\n" , 0 ) ;
# and get some info on the server
DevIo_SimpleWrite ( $ hash , "pref authorize ?\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "version ?\n" , 0 ) ;
DevIo_SimpleWrite ( $ hash , "serverstatus 0 200\n" , 0 ) ;
2017-05-28 19:22:04 +00:00
DevIo_SimpleWrite ( $ hash , "favorites items 0 " .
2015-03-19 20:32:45 +00:00
AttrVal ( $ name , "maxfavorites" , 100 ) . " want_url:1\n" , 0 ) ; # CD 0009 url mit abfragen
2014-11-09 18:01:21 +00:00
DevIo_SimpleWrite ( $ hash , "playlists 0 200\n" , 0 ) ;
2015-03-19 20:32:45 +00:00
DevIo_SimpleWrite ( $ hash , "alarm playlists 0 300\n" , 0 ) ; # CD 0011
2017-05-28 19:22:04 +00:00
DevIo_SimpleWrite ( $ hash , "apps 0 200\n" , 0 ) ; # CD 0029
2018-04-22 19:53:00 +00:00
DevIo_SimpleWrite ( $ hash , "pref httpport ?\n" , 0 ) ; # CD 0049
2017-05-28 19:22:04 +00:00
# CD 0032 start
if ( defined ( $ hash - > { helper } { apps } ) ) {
my @ enabledApps = split ( ',' , AttrVal ( $ name , 'enablePlugins' , '' ) ) ;
foreach my $ app ( @ enabledApps ) {
if ( defined ( $ hash - > { helper } { apps } { $ app } ) ) {
DevIo_SimpleWrite ( $ hash , ( $ hash - > { helper } { apps } { $ app } { cmd } ) . " items 0 200\n" , 0 ) ;
}
}
}
# CD 0032 end
2014-11-09 18:01:21 +00:00
return ( true ) ;
}
2017-06-04 10:03:33 +00:00
# CD 0038 start
sub SB_SERVER_RemoveInternalTimers ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
RemoveInternalTimer ( $ hash ) ;
RemoveInternalTimer ( "SB_SERVER_Alive:$name" ) ;
RemoveInternalTimer ( "StartTalk:$name" ) ;
RemoveInternalTimer ( "RecallAfterTalk:$name" ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendPlaylists:$name" ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendFavorites:$name" ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendSyncMasters:$name" ) ;
RemoveInternalTimer ( "SB_SERVER_tcb_SendAlarmPlaylists:$name" ) ;
RemoveInternalTimer ( "CheckConnection:$name" ) ;
}
# CD 0038 end
2015-03-19 20:32:45 +00:00
# CD 0006 start - added
# ----------------------------------------------------------------------------
# copied from DevIo.pm 7099
# ----------------------------------------------------------------------------
sub SB_SERVER_setStates ($$)
{
my ( $ hash , $ val ) = @ _ ;
$ hash - > { STATE } = $ val ;
setReadingsVal ( $ hash , "state" , $ val , TimeNow ( ) ) ;
}
# CD 0006 end
2014-11-09 18:01:21 +00:00
2017-05-28 19:22:04 +00:00
# ----------------------------------------------------------------------------
# load/save sync groups
#
# CD 0024
# ----------------------------------------------------------------------------
sub SB_SERVER_StatefileName ($$)
{
my ( $ name , $ prefix ) = @ _ ;
my $ statefile = $ attr { global } { statefile } ;
$ statefile = substr $ statefile , 0 , rindex ( $ statefile , '/' ) + 1 ;
return $ statefile . $ prefix . "_$name.dd.save" ;
}
sub SB_SERVER_SaveSyncGroups ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return "No saved syncgroups found" unless ( defined ( $ hash - > { helper } { syncGroups } ) ) ;
return "No statefile specified" if ( ! $ attr { global } { statefile } ) ;
my $ statefile = SB_SERVER_StatefileName ( $ name , 'sbsg' ) ;
if ( open ( FH , ">$statefile" ) ) {
my $ t = localtime ;
print FH "#$t\n" ;
my $ dumper = Data::Dumper - > new ( [] ) ;
$ dumper - > Terse ( 1 ) ;
$ dumper - > Values ( [ $ hash - > { helper } { syncGroups } ] ) ;
print FH $ dumper - > Dump ;
close ( FH ) ;
} else {
my $ msg = "SB_SERVER_SaveSyncGroups: Cannot open $statefile: $!" ;
Log3 $ hash , 1 , $ msg ;
}
return undef ;
}
sub SB_SERVER_LoadSyncGroups ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return "No statefile specified" if ( ! $ attr { global } { statefile } ) ;
my $ statefile = SB_SERVER_StatefileName ( $ name , 'sbsg' ) ;
if ( open ( FH , "<$statefile" ) ) {
my $ encoded ;
while ( my $ line = <FH> ) {
chomp $ line ;
next if ( $ line =~ m/^#.*$/ ) ;
$ encoded . = $ line ;
}
close ( FH ) ;
return if ( ! defined ( $ encoded ) ) ;
my $ decoded = eval $ encoded ;
$ hash - > { helper } { syncGroups } = $ decoded ;
} else {
my $ msg = "SB_SERVER_LoadSyncGroups: no syncgroups file found" ;
Log3 undef , 4 , $ msg ;
}
return undef ;
}
sub SB_SERVER_BuildPlayerList ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
delete ( $ hash - > { helper } { players } ) if ( defined ( $ hash - > { helper } { players } ) ) ;
# build player list
foreach my $ mydev ( keys % defs ) {
my $ iodevhash ;
if ( defined ( $ defs { $ mydev } { IODev } ) ) {
$ iodevhash = $ defs { $ mydev } { IODev } ;
if ( ( defined ( $ defs { $ mydev } { TYPE } ) ) &&
( $ defs { $ mydev } { TYPE } eq "SB_PLAYER" ) ) { # CD 0029 umsortiert
if ( ( defined ( $ iodevhash - > { NAME } ) ) && # CD 0029 umsortiert
( $ iodevhash - > { NAME } eq $ name ) ) {
# we found a valid entry
my $ chash = $ defs { $ mydev } ;
my $ fn = $ chash - > { NAME } ;
my $ ln = $ chash - > { PLAYERNAME } ;
if ( $ ln ne '?' ) {
if ( ! defined ( $ hash - > { helper } { players } { $ fn } ) ) {
$ hash - > { helper } { players } { $ fn } { fhemname } = $ fn ;
$ hash - > { helper } { players } { $ fn } { lmsname } = $ ln ;
$ hash - > { helper } { players } { $ fn } { mac } = $ chash - > { PLAYERMAC } ;
$ hash - > { helper } { players } { $ fn } { type } = 'FHEM' ;
}
if ( ! defined ( $ hash - > { helper } { players } { $ ln } ) ) {
$ hash - > { helper } { players } { $ ln } { fhemname } = $ fn ;
$ hash - > { helper } { players } { $ ln } { lmsname } = $ ln ;
$ hash - > { helper } { players } { $ ln } { mac } = $ chash - > { PLAYERMAC } ;
$ hash - > { helper } { players } { $ ln } { type } = 'LMS' ;
}
}
}
}
}
}
}
# ----------------------------------------------------------------------------
# load/save server state
#
# CD 0025
# ----------------------------------------------------------------------------
sub SB_SERVER_SaveServerStates ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return "No server states found" unless ( defined ( $ hash - > { helper } { savedServerStates } ) ) ;
return "No statefile specified" if ( ! $ attr { global } { statefile } ) ;
my $ statefile = SB_SERVER_StatefileName ( $ name , 'sbst' ) ;
if ( open ( FH , ">$statefile" ) ) {
my $ t = localtime ;
print FH "#$t\n" ;
my $ dumper = Data::Dumper - > new ( [] ) ;
$ dumper - > Terse ( 1 ) ;
$ dumper - > Values ( [ $ hash - > { helper } { savedServerStates } ] ) ;
print FH $ dumper - > Dump ;
close ( FH ) ;
} else {
my $ msg = "SB_SERVER_SaveServerState: Cannot open $statefile: $!" ;
Log3 $ hash , 1 , $ msg ;
}
return undef ;
}
sub SB_SERVER_LoadServerStates ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return "No statefile specified" if ( ! $ attr { global } { statefile } ) ;
my $ statefile = SB_SERVER_StatefileName ( $ name , 'sbst' ) ;
if ( open ( FH , "<$statefile" ) ) {
my $ encoded ;
while ( my $ line = <FH> ) {
chomp $ line ;
next if ( $ line =~ m/^#.*$/ ) ;
$ encoded . = $ line ;
}
close ( FH ) ;
return if ( ! defined ( $ encoded ) ) ;
my $ decoded = eval $ encoded ;
$ hash - > { helper } { savedServerStates } = $ decoded ;
} else {
my $ msg = "SB_SERVER_LoadServerState: no server state file found" ;
Log3 undef , 4 , $ msg ;
}
return undef ;
}
sub SB_SERVER_Save ($$) {
my ( $ hash , $ statename ) = @ _ ;
my $ name = $ hash - > { NAME } ;
$ statename = 'default' unless defined ( $ statename ) ;
Log3 ( $ hash , 3 , "SB_SERVER_Save($name): name: $statename" ) ;
delete ( $ hash - > { helper } { savedServerStates } { $ statename } ) if ( defined ( $ hash - > { helper } { savedServerStates } ) && defined ( $ hash - > { helper } { savedServerStates } { $ statename } ) ) ;
SB_SERVER_BuildPlayerList ( $ hash ) if ( ! defined ( $ hash - > { helper } { players } ) ) ;
foreach my $ e ( keys % { $ hash - > { helper } { players } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { players } { $ e } ) ) {
if ( $ hash - > { helper } { players } { $ e } { type } eq 'FHEM' ) {
if ( defined ( $ defs { $ e } ) ) {
my $ phash = $ defs { $ e } ;
$ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { mac } = $ hash - > { helper } { players } { $ e } { mac } ;
$ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { power } = ReadingsVal ( $ e , "power" , "off" ) ;
$ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { volume } = ReadingsVal ( $ e , "volume" , "0" ) ;
my $ sm = InternalVal ( $ e , "SYNCMASTER" , "none" ) ;
$ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } = $ sm ;
if ( ( $ sm eq 'none' ) || ( $ sm eq $ hash - > { helper } { players } { $ e } { mac } ) ) {
SB_PLAYER_Save ( $ phash , "xxx_sss_" . $ statename ) ;
}
}
}
}
}
}
}
sub SB_SERVER_Recall ($$) {
my ( $ hash , $ arg ) = @ _ ; # CD 0036
my $ name = $ hash - > { NAME } ;
my $ del = 0 ;
my $ delonly = 0 ;
my $ statename ;
my @ args = split " " , $ arg ;
if ( defined ( $ args [ 0 ] ) ) {
$ statename = $ args [ 0 ] ;
} else {
$ statename = 'default' ;
}
Log3 ( $ hash , 3 , "SB_SERVER_Recall($name): name: $statename" ) ;
# Optionen auswerten
for my $ opt ( @ args ) {
$ del = 1 if ( $ opt =~ m/^del$/ ) ;
$ delonly = 1 if ( $ opt =~ m/^delonly$/ ) ;
}
if ( defined ( $ hash - > { helper } { savedServerStates } ) && defined ( $ hash - > { helper } { savedServerStates } { $ statename } ) ) {
if ( $ delonly == 0 ) {
# unsync all & set power
foreach my $ e ( keys % { $ hash - > { helper } { savedServerStates } { $ statename } { players } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } ) ) {
my $ mac = $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { mac } ;
SB_SERVER_Write ( $ hash , $ mac . " sync -\n" , "" ) ;
if ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { power } eq 'on' ) {
SB_SERVER_Write ( $ hash , $ mac . " power 1\n" , "" ) ;
} else {
SB_SERVER_Write ( $ hash , $ mac . " power 0\n" , "" ) ;
}
}
}
}
# sync slaves & set volume
foreach my $ e ( keys % { $ hash - > { helper } { savedServerStates } { $ statename } { players } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } ) ) {
my $ mac = $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { mac } ;
if ( ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } ne 'none' )
&& ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } ne $ mac ) ) {
SB_SERVER_Write ( $ hash , $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } . " sync $mac\n" , "" ) ;
SB_SERVER_Write ( $ hash , "$mac mixer volume " . $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { volume } . "\n" , "" ) ;
}
}
}
}
# reload player states
foreach my $ e ( keys % { $ hash - > { helper } { savedServerStates } { $ statename } { players } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } ) ) {
my $ mac = $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { mac } ;
if ( ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } eq 'none' )
|| ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } { syncMaster } eq $ mac ) ) { # CD 0026 ne durch eq ersetzt
if ( defined ( $ defs { $ e } ) ) {
my $ phash = $ defs { $ e } ;
SB_PLAYER_Recall ( $ phash , "xxx_sss_" . $ statename ) ;
}
}
}
}
}
}
if ( ( $ del == 1 ) || ( $ delonly == 1 ) ) {
# delete
foreach my $ e ( keys % { $ hash - > { helper } { savedServerStates } { $ statename } { players } } ) {
if ( $ e ne '' ) {
if ( defined ( $ hash - > { helper } { savedServerStates } { $ statename } { players } { $ e } ) ) {
if ( defined ( $ defs { $ e } ) ) {
my $ phash = $ defs { $ e } ;
SB_PLAYER_Recall ( $ phash , "xxx_sss_" . $ statename . " delonly" ) ;
}
}
}
}
delete ( $ hash - > { helper } { savedServerStates } { $ statename } ) ;
}
}
}
# CD 0031 User und Passwort speichern/lesen, aus 72_FRITZBOX
sub SB_SERVER_storePassword ($$$)
{
my ( $ hash , $ user , $ password ) = @ _ ;
my $ index = $ hash - > { TYPE } . "_" . $ hash - > { NAME } . "_passwd" ;
my $ key = getUniqueId ( ) . $ index ;
my $ enc_pwd = "" ;
if ( eval "use Digest::MD5;1" )
{
$ key = Digest::MD5:: md5_hex ( unpack "H*" , $ key ) ;
$ key . = Digest::MD5:: md5_hex ( $ key ) ;
}
for my $ char ( split // , $ password )
{
my $ encode = chop ( $ key ) ;
$ enc_pwd . = sprintf ( "%.2x" , ord ( $ char ) ^ ord ( $ encode ) ) ;
$ key = $ encode . $ key ;
}
my $ err = setKeyValue ( $ hash - > { TYPE } . "_" . $ hash - > { NAME } . "_user" , $ user ) ;
return "error while saving the user - $err" if ( defined ( $ err ) ) ;
$ err = setKeyValue ( $ index , $ enc_pwd ) ;
return "error while saving the password - $err" if ( defined ( $ err ) ) ;
return "user and password successfully saved" ;
}
sub SB_SERVER_readPassword ($)
{
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ index = $ hash - > { TYPE } . "_" . $ hash - > { NAME } . "_passwd" ;
my $ key = getUniqueId ( ) . $ index ;
my ( $ user , $ password , $ err ) ;
( $ err , $ password ) = getKeyValue ( $ index ) ;
if ( defined ( $ err ) ) {
Log3 $ hash , 2 , "SB_SERVER_readPassword($name): unable to read SB_SERVER password: $err" ;
return undef ;
}
my $ dec_pwd = '' ;
if ( defined ( $ password ) ) {
if ( eval "use Digest::MD5;1" ) {
$ key = Digest::MD5:: md5_hex ( unpack "H*" , $ key ) ;
$ key . = Digest::MD5:: md5_hex ( $ key ) ;
}
for my $ char ( map { pack ( 'C' , hex ( $ _ ) ) } ( $ password =~ /(..)/g ) ) {
my $ decode = chop ( $ key ) ;
$ dec_pwd . = chr ( ord ( $ char ) ^ ord ( $ decode ) ) ;
$ key = $ decode . $ key ;
}
} else {
Log3 $ hash , 2 , "SB_SERVER_readPassword($name): No password found" ;
return undef ;
}
( $ err , $ user ) = getKeyValue ( $ hash - > { TYPE } . "_" . $ hash - > { NAME } . "_user" ) ;
if ( defined ( $ err ) ) {
Log3 $ hash , 2 , "SB_SERVER_readPassword($name): unable to read SB_SERVER user: $err" ;
return undef ;
}
if ( defined ( $ user ) ) {
return ( $ user , $ dec_pwd )
} else {
Log3 $ hash , 2 , "SB_SERVER_readPassword($name): No user found" ;
return undef ;
}
}
# CD 0031 end
2014-11-09 18:01:21 +00:00
# ############################################################################
# No PERL code beyond this line
# ############################################################################
2014-07-06 22:13:50 +00:00
1 ;
= pod
2017-05-28 19:22:04 +00:00
= item device
2016-10-01 16:43:31 +00:00
= item summary connect to a Logitech Media Server ( LMS )
2017-05-28 19:22:04 +00:00
= item summary_DE Anbindung an Logitech Media Server ( LMS )
2014-11-09 18:01:21 +00:00
= begin html
< a name = "SB_SERVER" > </a>
<h3> SB_SERVER </h3>
<ul>
< a name = "SBserverdefine" > </a>
<b> Define </b>
<ul>
2018-04-22 19:53:00 +00:00
<code> define & lt ; name & gt ; SB_SERVER & lt ; ip | hostname [ : cliserverport ] & gt ; [ RCC: & lt ; RCC & gt ; ] [ WOL: & lt ; WOL & gt ; [ : & lt ; command & gt ; [ : & lt ; value & gt ; ] ] ] [ PRESENCE: & lt ; PRESENCE & gt ; [ : & lt ; reading & gt ; [ : & lt ; value for present & gt ; [ : & lt ; value for absent & gt ; ] ] ] ] [ USER: & lt ; username & gt ; ] [ PASSWORD: & lt ; password & gt ; ] </code>
2014-11-09 18:01:21 +00:00
<br> <br>
2016-10-01 16:43:31 +00:00
This module allows you in combination with the module SB_PLAYER to control a
Logitech Media Server ( LMS ) and connected Squeezebox Media Players . <br> <br>
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
Attention: The <code> [ : cliserverport ] </code> parameter is
optional . You just need to configure it if you changed it on the LMS .
2017-05-28 19:22:04 +00:00
The default TCP port is 9090 . <br> <br>
2016-10-01 16:43:31 +00:00
<b> Optional </b>
<ul>
2014-11-09 18:01:21 +00:00
<li> <code> & lt ; [ RCC ] & gt ; </code> : You can define a FHEM RCC Device , if you want to wake it up when you set the SB_SERVER on . </li>
2018-04-22 19:53:00 +00:00
<li> <code> & lt ; [ WOL ] & gt ; </code> : You can define a FHEM WOL Device , if you want to wake it up when you set the SB_SERVER on .
Command and value can be optionally configured , by default command is empty and 'on' is used as value . </li>
<li> <code> & lt ; [ PRESENCE ] & gt ; </code> : You can define a FHEM PRESENCE Device that is used to check if the server is reachable .
Optionally a reading and values for present and absent can be added ,
by default the reading 'state' and the values 'present' and 'absent' are used . </li>
2014-11-09 18:01:21 +00:00
<li> <code> & lt ; username & gt ; </code> and <code> & lt ; password & gt ; </code> : If your LMS is password protected you can define the credentials here . </li>
2016-10-01 16:43:31 +00:00
</ul> <br>
</ul>
2014-11-09 18:01:21 +00:00
< a name = "SBserverset" > </a>
<b> Set </b>
<ul>
<code> set & lt ; name & gt ; & lt ; command & gt ; </code>
<br> <br>
2016-10-01 16:43:31 +00:00
This module supports the following SB_Server related commands: <br> <br>
<ul>
<li> <b> abort </b> - Stops the connection to the server </li>
<li> <b> addToFHEMUpdate </b> - Includes the modules in the FHEM update , needs to be executed only once </li>
<li> <b> cliraw & lt ; cli - command & gt ; </b> - Sends a & lt ; cli - command & gt ; to the LMS CLI </li>
<li> <b> on </b> - Tries to switch on the Server by WOL or RCC </li>
<li> <b> removeFromFHEMUpdate </b> - Removes the modules from the FHEM update </li>
<li> <b> renew </b> - Renews the connection to the server </li>
<li> <b> rescan </b> - Starts the scan of the music library of the server </li>
<li> <b> statusRequest </b> - Update of readings from server and configured players </li>
2017-05-28 19:22:04 +00:00
<li> <b> save [ & lt ; name & gt ; ] </b> - Save all players state </li>
<li> <b> recall [ & lt ; name & gt ; ] [ options ] </b> - recall all players state <br> Options: </li>
<ul>
<li> del - delete saved state after restore </li>
<li> delonly - delete saved state without restoring </li>
</ul>
</ul>
<br> <br>
The command <code> syncGroup </code> can be used to manage group templates for synchronizing players . Each template
contains a list of players and can be activated on demand .
Possible subcommands: <br> <br>
<ul>
<li> <b> addp & lt ; playerName [ , playerName ... ] & gt ; & lt ; template & gt ; </b> - Add the specified player ( s ) to the template .
if the template doesn ' t exist , it is created automatically and the first player will be group master . </li>
<li> <b> removep & lt ; playerName [ , playerName ... ] & gt ; & lt ; template & gt ; </b> - Remove the specified player ( s ) from the
template . If one of the players was group master , the first remaining player will become new group master . </li>
<li> <b> masterp & lt ; playerName & gt ; & lt ; template & gt ; </b> - Change the group master . </li>
<li> <b> load [ poweron ] & lt ; template & gt ; </b> - Activate the template . The players of the template are unsynced from their
groups and added to a new group . With the optional keyword <code> poweron </code> , FHEM tries to power on all the players . </li>
<li> <b> delete & lt ; template & gt ; </b> - Delete the template . </li>
<li> <b> deleteall </b> - Delete all templates . </li>
<li> <b> talk & lt ; template & gt ; & lt ; text & gt ; </b> - Save the state of all the players , activate the template and output & lt ; text & gt ; using
the configured TTS provider . Restore the state of all the players after the end of the TTS output .
With the optional keyword <code> poweron </code> , FHEM tries to power on all the players . </li>
<li> <b> resetTTS </b> - Reset TTS output . </li>
<li> <b> volume & lt ; template & gt ; & lt ; n & gt ; </b> - Set the volume to & lt ; n & gt ; if the template is active . </li>
<li> <b> volume & lt ; template & gt ; + | - & lt ; n & gt ; </b> - Increase or decrease the volume by the given value if the template is active . </li>
</ul>
2016-10-01 16:43:31 +00:00
<br>
</ul>
< a name = "SBserverattr" > </a>
<b> Attributes </b>
<ul>
<li> <code> alivetimer & lt ; sec & gt ; </code> <br>
Default: 120 . Every & lt ; sec & gt ; seconds it is checked , whether the computer with its LMS is still reachable
2018-04-22 19:53:00 +00:00
– either via an internal ping ( that leads regulary to problems ) or via PRESENCE ( preferred , no problems )
2016-10-01 16:43:31 +00:00
- and running . </li>
<li> <code> doalivecheck & lt ; true | false & gt ; </code> <br>
Switches the LMS - monitoring on or off . </li>
2017-05-28 19:22:04 +00:00
<li> <code> enablePlugins & lt ; plugin1 [ , pluginX ] & gt ; </code> <br>
Adds the playlists and favorites ( if available ) of the specified LMS - plugins . </li>
2016-10-01 16:43:31 +00:00
<li> <code> httpport & lt ; port & gt ; </code> <br>
2018-04-22 19:53:00 +00:00
Normally the http - port is automatically detected . This attribute can be used to override the detected value .
You can check the port - number of the LMS within its setup under Setup – Network – Web Server Port Number . </li>
2016-10-01 16:43:31 +00:00
<li> < a name = "SBserver_attribut_ignoredIPs" > <code> ignoredIPs & lt ; IP - Address [ , IP - Address ] & gt ; </code>
</a> < br />With this attribute you can define IP-addresses of players which will to be ignored by the server, e.g. "192.168.0.11,192.168.0.37"</ li >
<li> < a name = "SBserver_attribut_ignoredMACs" > <code> ignoredMACs & lt ; MAC - Address [ , MAC - Address ] & gt ; </code>
</a> < br />With this attribute you can define MAC-addresses of players which will to be ignored by the server, e.g. "00:11:22:33:44:55,ff:ee:dd:cc:bb:aa"</ li >
2017-05-28 19:22:04 +00:00
<li> <code> internalPingProtocol icmp | tcp | udp | syn | stream | none </code> <br>
Specifies the protocol for the internal ping , default is tcp . </li>
2016-10-01 16:43:31 +00:00
<li> <code> maxcmdstack & lt ; quantity & gt ; </code> <br>
By default the stack ist set up to 200 . If the connection to the LMS is lost , up to & lt ; quantity & gt ;
commands are buffered . After the link is reconnected , commands , that are not older than five minutes ,
are send to the LMS . </li>
<li> <code> maxfavorites & lt ; number & gt ; </code> <br>
Adjust here the maximal number of the favourites . </li>
</ul>
2014-11-09 18:01:21 +00:00
</ul>
= end html
2016-10-01 16:43:31 +00:00
= begin html_DE
< a name = "SB_SERVER" > </a>
<h3> SB_SERVER </h3>
<ul>
< a name = "SBserverdefine" > </a>
<b> Define </b>
<ul>
2018-04-22 19:53:00 +00:00
<code> define & lt ; name & gt ; SB_SERVER & lt ; ip | hostname [ : cliserverport ] & gt ; [ RCC: & lt ; RCC & gt ; ] [ WOL: & lt ; WOL & gt ; [ : & lt ; Befehl & gt ; [ : & lt ; Wert & gt ; ] ] ] [ PRESENCE: & lt ; PRESENCE & gt ; [ : & lt ; Reading & gt ; [ : & lt ; Wert für anwesend & gt ; [ : & lt ; Wert für abwesend & gt ; ] ] ] ] [ USER: & lt ; Benutzername & gt ; ] [ PASSWORD: & lt ; Passwort & gt ; ] </code>
2016-10-01 16:43:31 +00:00
<br> <br>
Diese Modul erm & ouml ; glicht es - zusammen mit dem Modul SB_PLAYER - einen
Logitech Media Server ( LMS ) und die angeschlossenen Squeezebox Media
Player zu steuern . <br> <br>
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
Achtung: Die Angabe des Parameters <code> [ : cliserverport ] </code> ist
optional und nur dann erforderlich , wenn die Portnummer im LMS vom
Standardwert ( TCP Port 9090 ) abweichend eingetragen wurde . <br> <br>
2017-05-28 19:22:04 +00:00
2016-10-01 16:43:31 +00:00
<b> Optionen </b>
<ul>
<li> <code> & lt ; [ RCC ] & gt ; </code> : Hier kann ein FHEM RCC Device angegeben werden mit dem der Server aufgeweckt und eingeschaltet werden kann . </li>
2018-04-22 19:53:00 +00:00
<li> <code> & lt ; [ WOL ] & gt ; </code> : Hier kann ein FHEM WOL Device angegeben werden mit dem der Server aufgeweckt und eingeschaltet werden kann .
Optional k & ouml ; nnen Befehl und Wert mit angegeben werden , voreingestellt ist kein Befehl und der Wert 'on' . </li>
<li> <code> & lt ; [ PRESENCE ] & gt ; </code> : Hier kann ein FHEM PRESENCE Device angegeben
werden mit dem die Erreichbarkeit des Servers & uuml ; berpr & uuml ; ft werden kann .
Optional k & ouml ; nnen Reading und Werte f & uuml ; r an / abwesend mit angegeben werden ,
voreingestellt ist das Reading 'state' und die Werte 'present' und 'absent' . </li>
<li> <code> & lt ; Benutzername & gt ; </code> und <code> & lt ; Passwort & gt ; </code> : Falls der Server durch ein Passwort gesichert wurde , k & ouml ; nnen hier die notwendigen Angaben für den Serverzugang angegeben werden . </li>
2016-10-01 16:43:31 +00:00
</ul> <br>
</ul>
< a name = "SBserverset" > </a>
<b> Set </b>
<ul>
<code> set & lt ; name & gt ; & lt ; command & gt ; </code>
<br> <br>
Dieses Modul unterst & uuml ; tzt folgende SB_SERVER relevanten Befehle: <br> <br>
<ul>
<li> <b> abort </b> - Bricht die Verbindung zum Server ab . </li>
<li> <b> addToFHEMUpdate </b> - F & uuml ; gt die Module dem FHEM - Update hinzu , muss nur einmalig ausgef & uuml ; hrt werden . </li>
<li> <b> cliraw & lt ; cli - command & gt ; </b> - Sendet einen CLI - Befehl an das LMS CLI </li>
<li> <b> on </b> - Versucht den Server per WOL oder RCC einzuschalten . </li>
<li> <b> removeFromFHEMUpdate </b> - Schlie & szlig ; t die Module vom FHEM - Update aus . </li>
<li> <b> renew </b> - Erneuert die Verbindung zum Server . </li>
<li> <b> rescan </b> - Startet einen Scan der Musikbibliothek f & uuml ; r alle im Server angegebenen Verzeichnisse . </li>
<li> <b> statusRequest </b> - Aktualisiert die Readings von Server und konfigurierten Playern . </li>
2017-05-28 19:22:04 +00:00
<li> <b> save [ & lt ; name & gt ; ] </b> - Speichert den Zustand aller Player unter dem Namen & lt ; name & gt ; ab . </li>
<li> <b> recall [ & lt ; name & gt ; ] [ options ] </b> - Ruft den Zustand aller Player auf . <br> Optionen: </li>
<ul>
<li> del - L & ouml ; scht nach dem Restore den gespeicherten Status </li>
<li> delonly - L & ouml ; scht den gespeicherten Status ohne vorherigem Restore </li>
</ul>
</ul>
<br> <br>
2018-04-22 19:53:00 +00:00
Der Befehl <code> syncGroup </code> dient zur Verwaltung von Gruppenvorlagen für die Synchronisierung der Player .
2017-05-28 19:22:04 +00:00
Dar & uuml ; ber k & ouml ; nnen Gruppen von Playern angelegt werden die sich bei Bedarf aktivieren lassen . Die Vorlagen
werden bei SAVE und SHUTDOWN von FHEM abgespeichert und beim Start von FHEM geladen .
Folgende Unterbefehle sind definiert: <br> <br>
<ul>
<li> <b> addp & lt ; playerName [ , playerName ... ] & gt ; & lt ; Vorlage & gt ; </b> - F & uuml ; gt die angegebenen Player zur Vorlage hinzu , wenn die
Vorlage noch nicht existiert wird sie angelegt und der erste Player wird Master . </li>
<li> <b> removep & lt ; playerName [ , playerName ... ] & gt ; & lt ; Vorlage & gt ; </b> - Entfernt die angegebenen Player aus der Vorlage , falls
einer Master war wird der 1 . verbleibende Player der Vorlage zum neuen Master . </li>
<li> <b> masterp & lt ; playerName & gt ; & lt ; Vorlage & gt ; </b> - Legt den angegebenen Player als Master fest . </li>
<li> <b> load [ poweron ] & lt ; Vorlage & gt ; </b> - Aktiviert die angegebene Vorlage , die betroffenen Player werden entsynchronisiert
und zu einer neuen Gruppe zusammengef & uuml ; gt . Wenn zus & auml ; tzlich <code> poweron </code> angegeben wird , werden die Player
eingeschaltet . </li>
<li> <b> delete & lt ; Vorlage & gt ; </b> - L & ouml ; scht die angegebene Vorlage . </li>
<li> <b> deleteall </b> - L & ouml ; scht alle Gruppenvorlagen . </li>
<li> <b> talk & lt ; Vorlage & gt ; & lt ; Text & gt ; </b> - Speichert den Zustand aller Player ab , aktiviert die angegebene Vorlage und spielt
den Text & uuml ; ber den beim Player konfigurierten TTS - Dienst ab . Nach Ende der Durchsage wird der vorherige Zustand der Player wieder
hergestellt . Wenn zus & auml ; tzlich <code> poweron </code> angegeben wird , wird versucht die Player einzuschalten . </li>
<li> <b> resetTTS </b> - TTS zur & uuml ; cksetzen , kann n & ouml ; tig sein wenn die Ausgabe h & auml ; ngt . </li>
<li> <b> volume & lt ; Vorlage & gt ; & lt ; n & gt ; </b> - Stellt die Lautst & auml ; rke auf einen Wert & lt ; n & gt ; ein . Dabei muss & lt ; n & gt ;
2018-04-22 19:53:00 +00:00
eine Zahl zwischen 0 und 100 sein . Der Befehl wird nur ausgeführt wenn die Vorlage aktiv ist . </li>
2017-05-28 19:22:04 +00:00
<li> <b> volume & lt ; Vorlage & gt ; + | - & lt ; n & gt ; </b> - Erh & ouml ; ht oder vermindert die Lautst & auml ; rke um den Wert , der durch + | - & lt ; n & gt ;
2018-04-22 19:53:00 +00:00
vorgegeben wird . Dabei muss & lt ; n & gt ; eine Zahl zwischen 0 und 100 sein . Der Befehl wird nur ausgeführt wenn die Vorlage aktiv ist . </li>
2017-05-28 19:22:04 +00:00
</ul>
2016-10-01 16:43:31 +00:00
<br>
</ul>
< a name = "SBserverattr" > </a>
<b> Attribute </b>
<ul>
<li> <code> alivetimer & lt ; sec & gt ; </code> <br>
Default 120 . Alle & lt ; sec & gt ; Sekunden wird & uuml ; berpr & uuml ; ft , ob der Rechner mit dem LMS noch erreichbar ist
2018-04-22 19:53:00 +00:00
- entweder über internen Ping ( f & uuml ; hrt zu regelm & auml ; & szlig ; igen H & auml ; ngern von FHEM ) oder PRESENCE ( bevorzugt ,
2016-10-01 16:43:31 +00:00
keine H & auml ; nger ) - und ob der LMS noch l & auml ; uft . </li>
<li> <code> doalivecheck & lt ; true | false & gt ; </code> <br>
& Uuml ; berwachung des LMS ein - oder auschalten . </li>
2017-05-28 19:22:04 +00:00
<li> <code> enablePlugins & lt ; plugin1 [ , pluginX ] & gt ; </code> <br>
Bindet die Wiedergabelisten und Favoriten ( soweit vorhanden ) von LMS - Plugins ( z . B . Spotify ) ein . </li>
2016-10-01 16:43:31 +00:00
<li> <code> httpport & lt ; port & gt ; </code> <br>
2018-04-22 19:53:00 +00:00
Im Normalfall wird der http - Port automatisch ermittelt . Sollte dies NICHT funktionieren kann er über das Attribut fest vorgegeben werden .
Zur & Uuml ; berpr & uuml ; fung kann im Server unter Einstellungen – Erweitert – Netzwerk
2016-10-01 16:43:31 +00:00
- Anschlussnummer des Webservers nachgeschlagen werden . </li>
<li> < a name = "SBserver_attribut_ignoredIPs" > <b> <code> ignoredIPs & lt ; IP - Adresse & gt ; [ , IP - Adresse ] </code> </b>
2018-04-22 19:53:00 +00:00
</a> < br />Mit diesem Attribut kann die automatische Erkennung dedizierter Geräte durch die Angabe derer IP-Adressen unterdrückt werden, z.B. "192.168.0.11,192.168.0.37"</ li >
2016-10-01 16:43:31 +00:00
<li> < a name = "SBserver_attribut_ignoredMACs" > <b> <code> ignoredMACs & lt ; MAC - Adresse & gt ; [ , MAC - Adresse ] </code> </b>
2018-04-22 19:53:00 +00:00
</a> < br />Mit diesem Attribut kann die automatische Erkennung dedizierter Geräte durch die Angabe derer MAC-Adressen unterdrückt werden, z.B. "00:11:22:33:44:55,ff:ee:dd:cc:bb:aa"</ li >
2017-05-28 19:22:04 +00:00
<li> <code> internalPingProtocol icmp | tcp | udp | syn | stream | none </code> <br>
2018-04-22 19:53:00 +00:00
Legt fest welches Protokoll für den internen Ping verwendet wird . Wenn das Attribut nicht definiert ist , wird tcp verwendet . </li>
2016-10-01 16:43:31 +00:00
<li> <code> maxcmdstack & lt ; Anzahl & gt ; </code> <br>
Default ist der Stack auf eine Gr & ouml ; & szlig ; e von 200 eingestellt . Wenn die Verbindung zum LMS unterbrochen ist ,
werden bis zu & lt ; Anzahl & gt ; Befehle zwischengespeichert . Nach dem Verbindungsaufbau werden die Befehle ,
die nicht & auml ; lter als 5 Minuten sind , an den LMS geschickt . </li>
<li> <code> maxfavorites & lt ; Anzahl & gt ; </code> <br>
Die maximale Anzahl der Favoriten wird hier eingestellt . </li>
</ul>
</ul>
= end html_DE
2014-11-09 18:01:21 +00:00
= cut