├── README.md ├── VERSION ├── ccu1 ├── newudp └── update_addon ├── ccu2 ├── newudp └── update_addon ├── ccu3 ├── newudp └── update_addon ├── ccu3x86 ├── newudp └── update_addon ├── generate_img.sh ├── rc.d └── sonos2 ├── sonos2 ├── common.tcl ├── sonos2.tcl ├── sonos2_addon.cfg └── stop.tcl ├── src └── newudp │ ├── Makefile │ └── newudp.c ├── update_script └── www ├── ZoneGroupState.txt ├── index.html ├── phpinc.php ├── public ├── css │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── bootstrapValidator.min.css │ ├── custom.css │ └── custombootstrap.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── Preloader.gif │ ├── Preloader1.gif │ ├── logo.png │ ├── pause.png │ ├── play.png │ └── stop.png └── js │ ├── bootstrap.min.js │ └── bootstrapValidator.min.js ├── server.cgi ├── settings ├── ZoneGroupState.txt ├── settings.cgi ├── settings.html ├── udp.cgi └── udp.html ├── sonos2.cgi ├── sonos2inc.tcl ├── status.cgi ├── status.html └── update-check.cgi /README.md: -------------------------------------------------------------------------------- 1 | # HomeMatic Sonos Player Addon 2 | This repository hosts the development on a HomeMatic CCU-Addon for enabling a CCU to control Sono type audio players (www.sonos.com) from within the WebUI user interface. 3 | 4 | ## Supported CCU models 5 | * [HomeMatic CCU3](https://www.eq-3.de/produkte/homematic/zentralen-und-gateways/smart-home-zentrale-ccu3.html) / [RaspberryMatic](http://raspberrymatic.de/) 6 | * [HomeMatic CCU2](https://www.eq-3.de/produkt-detail-zentralen-und-gateways/items/homematic-zentrale-ccu-2.html) 7 | * HomeMatic CCU1 8 | 9 | ## Installation 10 | 1. Download the latest Addon release (https://github.com/homematic-community/hm-sonos/releases) 11 | 2. Upload it via the HomeMatic WebUI provided Addon functionality. 12 | 3. Login to your CCU and configure the Sonos Player Addon accordingly. 13 | 14 | ## Support 15 | If you encounter any problems or have any idea for enhancements of this addon please create a corresponding ticket at the issue tracker (https://github.com/homematic-community/hm-sonos/issues). For getting direct help a german speaking discussion fora is hosting a seperate discussion thread as well: http://homematic-forum.de/forum/viewtopic.php?f=41&t=26531 16 | 17 | ## License 18 | The Sonos CCU Addon as published in this Github repository is provided under the GPL license. 19 | 20 | ## Authors 21 | * The main developer of the addon is 'fiveyears' with the build environment and ports to the CCU1 and RaspberryMatic platform being performed by Jens Maus. 22 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.9 2 | -------------------------------------------------------------------------------- /ccu1/newudp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu1/newudp -------------------------------------------------------------------------------- /ccu1/update_addon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu1/update_addon -------------------------------------------------------------------------------- /ccu2/newudp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu2/newudp -------------------------------------------------------------------------------- /ccu2/update_addon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu2/update_addon -------------------------------------------------------------------------------- /ccu3/newudp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu3/newudp -------------------------------------------------------------------------------- /ccu3/update_addon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu3/update_addon -------------------------------------------------------------------------------- /ccu3x86/newudp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu3x86/newudp -------------------------------------------------------------------------------- /ccu3x86/update_addon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/ccu3x86/update_addon -------------------------------------------------------------------------------- /generate_img.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tar=$(which gtar) # OSX gnu tar 3 | if [ -z $tar ]; then 4 | tar="tar" 5 | fi 6 | mkdir -p tmp 7 | cp -a sonos2 tmp/ 8 | cp -a rc.d tmp/ 9 | cp -a www tmp/ 10 | cp -a ccu1 tmp/ 11 | cp -a ccu2 tmp/ 12 | cp -a ccu3 tmp/ 13 | cp -a ccu3x86 tmp/ 14 | cp -a update_script tmp/ 15 | cp -a VERSION tmp/sonos2/ 16 | cd tmp 17 | 18 | $tar --owner=root --group=root --exclude=.DS_Store -czvf ../sonos2-addon-$(cat ../VERSION).tar.gz * 19 | cd .. 20 | rm -rf tmp 21 | -------------------------------------------------------------------------------- /rc.d/sonos2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ADDONNAME=sonos2 4 | RCDDIR=/usr/local/etc/config/rc.d 5 | ADDONDIR=/usr/local/etc/config/addons/${ADDONNAME} 6 | WWWDIR=/usr/local/etc/config/addons/www/${ADDONNAME} 7 | 8 | AUTOSTART_SCRIPT=${ADDONNAME} 9 | START_SCRIPT=${ADDONNAME}.tcl 10 | STOP_SCRIPT=stop.tcl 11 | CONFIG_URL=/addons/${ADDONNAME}/settings/settings.cgi 12 | 13 | # check for unsupported platforms 14 | if grep -qim1 busmatic /www/api/methods/ccu/downloadFirmware.tcl; then 15 | exit 13 16 | fi 17 | 18 | # change to addon directory 19 | cd ${ADDONDIR} 20 | 21 | case "$1" in 22 | ""|start) 23 | tclsh $START_SCRIPT & 24 | ;; 25 | 26 | info) 27 | VER=$(cat ${ADDONDIR}/VERSION) 28 | echo "Version: ${VER}" 29 | echo "Info: Sonos Player CCU Addon
" 30 | echo "Info: Copyright (c) 2014-2021 fiveyears, Jens Maus
" 31 | echo "Info: https://github.com/homematic-community/hm-sonos" 32 | echo "Name: Sonos Player" 33 | echo "Operations: uninstall restart" 34 | echo "Config-Url: $CONFIG_URL" 35 | echo "Update: /addons/${ADDONNAME}/update-check.cgi" 36 | ;; 37 | 38 | restart) 39 | tclsh $STOP_SCRIPT 40 | tclsh $START_SCRIPT & 41 | ;; 42 | 43 | stop) 44 | tclsh $STOP_SCRIPT 45 | ;; 46 | 47 | uninstall) 48 | tclsh $STOP_SCRIPT 49 | cd ${RCDDIR} 50 | 51 | ${WWWDIR}/bin/update_addon ${ADDONNAME} 52 | rm -rf ${ADDONDIR} 53 | rm -rf ${WWWDIR} 54 | rm -rf ${AUTOSTART_SCRIPT} 55 | ;; 56 | 57 | *) 58 | echo "usage:" 59 | echo " ${ADDONNAME} [info|start|stop|restart|uninstall]" 60 | ;; 61 | esac 62 | -------------------------------------------------------------------------------- /sonos2/common.tcl: -------------------------------------------------------------------------------- 1 | ## 2 | # common.tcl 3 | # Enthält gemeinsam verwendete Funktionen und Konstanten. 4 | # 5 | # @author F. Werner 6 | ## 7 | 8 | #******************************************************************************* 9 | # Allgemeine Konstanten 10 | #******************************************************************************* 11 | 12 | ## 13 | # Name des Addons. 14 | ## 15 | set ADDON_NAME "sonos2" 16 | #******************************************************************************* 17 | # Logmeldungen 18 | #******************************************************************************* 19 | 20 | ## 21 | # Loggt eine Nachricht. 22 | # @param message zu loggende Nachricht 23 | ## 24 | proc log { message } { 25 | global ADDON_NAME 26 | 27 | exec logger "$ADDON_NAME - $message" 28 | } 29 | 30 | #******************************************************************************* 31 | # Dateizugriff 32 | #******************************************************************************* 33 | 34 | ## 35 | # Speichert Daten in einer Datei. 36 | # @param fileName Name der Datei 37 | # @param Daten, die in die Datei geschrieben werden sollen 38 | ## 39 | proc saveToFile { fileName content } { 40 | set fd -1 41 | 42 | set fd [open $fileName w] 43 | if { $fd != -1 } then { 44 | puts -nonewline $fd $content 45 | close $fd 46 | } else { 47 | error "could not write file $fileName" 48 | } 49 | } 50 | 51 | ## 52 | # Liefert den Inhalt einer Datei. 53 | # @param fileName Name der Datei 54 | # @return Inhalt der Datei 55 | ## 56 | proc loadFromFile { fileName } { 57 | set fd -1 58 | 59 | set fd [open $fileName r] 60 | if { $fd != -1 } then { 61 | set result [read $fd] 62 | } else { 63 | error "could not read file $fileName" 64 | } 65 | 66 | return $result 67 | } 68 | 69 | #******************************************************************************* 70 | # PID-Datei 71 | #******************************************************************************* 72 | 73 | ## 74 | # Name der PID-Datei. 75 | ## 76 | set PID_FILE "$ADDON_NAME.pid" 77 | 78 | ## 79 | # Ermittelt, ob die Zusatzsoftware gerade läuft. 80 | # Es wird angenommen, dass die Zusatzsoftware läuft, solange die PID-Datei 81 | # existiert. 82 | # @return 1, falls die Zusatzsoftware gerade läuft 83 | ## 84 | proc isRunning { } { 85 | global PID_FILE 86 | return [file exists $PID_FILE] 87 | } 88 | 89 | ## 90 | # Schreibt die PID-Datei. 91 | # Die PID-Datei enthält die Prozess-Id der ausführenden Tcl-Instanz. 92 | ## 93 | proc writePidFile { } { 94 | global PID_FILE 95 | 96 | saveToFile $PID_FILE [pid] 97 | } 98 | 99 | ## 100 | # Liefert die PID aus der PID-Datei. 101 | # @return PID aus der PID-Datei 102 | ## 103 | proc readPidFile { } { 104 | global PID_FILE 105 | 106 | return [loadFromFile $PID_FILE] 107 | } 108 | 109 | ## 110 | # Löscht die PID-Datei 111 | ## 112 | proc removePidFile { } { 113 | global PID_FILE 114 | 115 | file delete $PID_FILE 116 | } 117 | 118 | -------------------------------------------------------------------------------- /sonos2/sonos2.tcl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/sonos2/sonos2.tcl -------------------------------------------------------------------------------- /sonos2/sonos2_addon.cfg: -------------------------------------------------------------------------------- 1 | { 2 | CONFIG_URL /addons/sonos2/settings/settings.cgi 3 | CONFIG_DESCRIPTION { 4 | de {
  • Sonos Player Addon Setup.
  • } 5 | en {
  • Sonos Player Addon Setup.
  • } 6 | } 7 | ID sonos2 8 | CONFIG_NAME "Sonos Player" 9 | } 10 | -------------------------------------------------------------------------------- /sonos2/stop.tcl: -------------------------------------------------------------------------------- 1 | #!/bin/tclsh 2 | 3 | ## 4 | # stop.tcl 5 | # Stoppt die Zusatzsoftware. 6 | # 7 | # @author F. Werner 8 | ## 9 | 10 | #******************************************************************************* 11 | # Includes 12 | #******************************************************************************* 13 | 14 | source common.tcl 15 | 16 | #******************************************************************************* 17 | # Funktionen 18 | #******************************************************************************* 19 | 20 | ## 21 | # Hauptprogramm. 22 | # Stoppt die Zusatzsoftware, sofern diese läuft. 23 | ## 24 | proc main { } { 25 | if { [isRunning] } then { 26 | set pid [readPidFile] 27 | 28 | catch { exec kill -KILL $pid } 29 | removePidFile 30 | } 31 | 32 | log "stopped" 33 | } 34 | 35 | #******************************************************************************* 36 | # Einsprungpunkt 37 | #******************************************************************************* 38 | 39 | if { [catch { main } errorMessage] } then { 40 | log $errorMessage 41 | exit 1 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/newudp/Makefile: -------------------------------------------------------------------------------- 1 | #****************************************************************** 2 | #** File: Makefile 3 | #** Description: the makefile for newudp program 4 | #****************************************************************** 5 | 6 | CROSS = /usr/bin/ 7 | 8 | CFLAGS = -O3 -Wall -pedantic -std=gnu99 9 | CC = $(CROSS)gcc 10 | STRIP = $(CROSS)strip --strip-unneeded 11 | 12 | all: newudp 13 | 14 | newudp: newudp.c 15 | $(CC) $(CFLAGS) -o newudp newudp.c 16 | $(STRIP) newudp 17 | 18 | clean: 19 | rm -f newudp 20 | -------------------------------------------------------------------------------- /src/newudp/newudp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define BUF_SIZE 1024 13 | #define SERVER "239.255.255.250" 14 | #define PORT 1900 //The port on which to send data 15 | #define TIMEOUT 5 16 | #define MAX_ERROR_MSG 0x1000 17 | 18 | static int compile_regex (regex_t * r, const char * regex_text) 19 | { 20 | int status = regcomp (r, regex_text, REG_EXTENDED|REG_ICASE); 21 | if (status != 0) { 22 | char error_message[MAX_ERROR_MSG]; 23 | regerror (status, r, error_message, MAX_ERROR_MSG); 24 | printf ("Regex error compiling '%s': %s\n", 25 | regex_text, error_message); 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | /* 32 | Match the string in "to_match" against the compiled regular 33 | expression in "r". 34 | */ 35 | 36 | static int match_regex (regex_t * r, const char * to_match) 37 | { 38 | /* "P" is a pointer into the string which points to the end of the 39 | previous match. */ 40 | const char * p = to_match; 41 | /* "N_matches" is the maximum number of matches allowed. */ 42 | const int n_matches = 1; 43 | /* "M" contains the matches found. */ 44 | regmatch_t m[n_matches]; 45 | 46 | int nomatch = regexec (r, p, n_matches, m, 0); 47 | if (nomatch) { 48 | return 0; 49 | } 50 | return 1; 51 | } 52 | 53 | int main(int argc, const char * argv[]) { 54 | struct timeval timeout; 55 | timeout.tv_sec = TIMEOUT; 56 | timeout.tv_usec = 0; 57 | 58 | regex_t r; 59 | const char * regex_text; 60 | regex_text = "Sonos"; 61 | compile_regex(& r, regex_text); 62 | socklen_t len = sizeof(struct sockaddr_in); 63 | char buf[BUF_SIZE]; 64 | struct hostent *host; 65 | int n, s; 66 | char message[] = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: urn:schemas-upnp-org:device:ZonePlayer:1\r\nUSER-AGENT: UDAP/2.0\r\n\r\n"; 67 | 68 | 69 | host = gethostbyname(SERVER); 70 | if (host == NULL) { 71 | perror("gethostbyname"); 72 | return 1; 73 | } 74 | 75 | 76 | /* initialize socket */ 77 | if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 78 | perror("socket"); 79 | return 1; 80 | } 81 | if (setsockopt (s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, 82 | sizeof(timeout)) < 0) 83 | perror("setsockopt failed\n"); 84 | 85 | if (setsockopt (s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, 86 | sizeof(timeout)) < 0) 87 | perror("setsockopt failed\n"); struct sockaddr_in server; 88 | 89 | /* initialize server addr */ 90 | memset((char *) &server, 0, sizeof(struct sockaddr_in)); 91 | server.sin_family = AF_INET; 92 | server.sin_port = htons(PORT); 93 | server.sin_addr = *((struct in_addr*) host->h_addr); 94 | 95 | /* send message */ 96 | if (sendto(s, message, strlen(message), 0, (struct sockaddr *) &server, len) == -1) { 97 | perror("sendto()"); 98 | return 1; 99 | } 100 | 101 | /* receive echo. 102 | ** for single message, "while" is not necessary. But it allows the client 103 | ** to stay in listen mode and thus function as a "server" - allowing it to 104 | ** receive message sent from any endpoint. 105 | */ 106 | while ((n = recvfrom(s, buf, BUF_SIZE, 0, (struct sockaddr *) &server, &len)) != -1) { 107 | /*printf("Received from %s:%d: ", 108 | inet_ntoa(server.sin_addr), 109 | ntohs(server.sin_port)); */ 110 | fflush(stdout); 111 | if(write(1, buf, n) == -1 || 112 | write(1, "\n", 1) == -1) 113 | { 114 | return 1; 115 | } 116 | 117 | int i = match_regex(& r, buf); 118 | if ( i) { 119 | regfree (& r); 120 | close(s); 121 | return 0; 122 | } 123 | } 124 | regfree (& r); 125 | close(s); 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /update_script: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ADDONNAME=sonos2 4 | RCDDIR=/usr/local/etc/config/rc.d 5 | ADDONDIR=/usr/local/etc/config/addons/${ADDONNAME} 6 | WWWDIR=/usr/local/etc/config/addons/www/${ADDONNAME} 7 | 8 | # check for unsupported platforms 9 | if grep -qim1 busmatic /www/api/methods/ccu/downloadFirmware.tcl; then 10 | exit 13 11 | fi 12 | 13 | if [ "$1" == "" ]; then 14 | echo "CCU1" 15 | lcdtool "Installing Sonos... " 16 | mount -t yaffs /dev/mtdblock3 /usr/local 17 | elif [ "$1" == "CCU2" ]; then 18 | echo "CCU2" 19 | mount -t ubifs ubi1:user /usr/local 20 | elif [ "$1" == "HM-RASPBERRYMATIC" ]; then 21 | echo "HM-RASPBERRYMATIC" 22 | mount /usr/local 23 | fi 24 | 25 | # copy rc.d script 26 | mkdir -p ${RCDDIR} 27 | chmod 755 ${RCDDIR} 28 | cp rc.d/${ADDONNAME} ${RCDDIR}/ 29 | chmod +x ${RCDDIR}/${ADDONNAME} 30 | 31 | # copy web configuration pages 32 | mkdir -p ${WWWDIR} 33 | chmod 755 ${WWWDIR} 34 | cp -R www/* ${WWWDIR}/ 35 | 36 | # copy addon directory 37 | mkdir -p ${ADDONDIR} 38 | chmod 755 ${ADDONDIR} 39 | cp -R ${ADDONNAME}/* ${ADDONDIR} 40 | 41 | # copy CCU specific binaries to the bin directory 42 | mkdir -p ${WWWDIR}/bin 43 | if [ "$1" == "" ]; then 44 | cp -a ccu1/* ${WWWDIR}/bin/ 45 | elif [ "$1" == "CCU2" ]; then 46 | cp -a ccu2/* ${WWWDIR}/bin/ 47 | elif [ "$1" == "HM-RASPBERRYMATIC" ]; then 48 | if [ "$(uname -m)" == "i686" ] || [ "$(uname -m)" == "x86_64" ]; then 49 | cp -a ccu3x86/* ${WWWDIR}/bin/ 50 | else 51 | cp -a ccu3/* ${WWWDIR}/bin/ 52 | fi 53 | fi 54 | 55 | # add menu entry 56 | touch /usr/local/etc/config/hm_addons.cfg 57 | ${WWWDIR}/bin/update_addon sonos2 ${ADDONDIR}/sonos2_addon.cfg 58 | 59 | # sync filesystem to make sure all changes are written to disk 60 | sync 61 | 62 | if [ "$1" = "" ]; then 63 | echo "CCU1" 64 | lcdtool "Reboot... " 65 | lcdtool -a 0x40 -t bin 00 66 | echo "x" > /dev/watchdog 67 | reboot 68 | while true ; do true ; done 69 | elif [ "$1" = "CCU2" ]; then 70 | echo "CCU2" 71 | # CCU2 always reboots after Addon/Firmware Update 72 | elif [ "$1" = "HM-RASPBERRYMATIC" ]; then 73 | echo "HM-RASPBERRYMATIC" 74 | # RASPBERRYMATIC always reboots after Addon/Firmware Update 75 | fi 76 | -------------------------------------------------------------------------------- /www/ZoneGroupState.txt: -------------------------------------------------------------------------------- 1 | Küche,192.168.1.103,TV-Zimmer,192.168.1.101,Wohnzimmer,192.168.1.105 2 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | Page Redirection 10 | 11 | 12 | If you are not redirected automatically, follow the link to sonos 13 | 14 | -------------------------------------------------------------------------------- /www/public/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /www/public/css/bootstrapValidator.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * 4 | * A jQuery plugin to validate form fields. Use with Bootstrap 3 5 | * 6 | * @version v0.4.2 7 | * @author https://twitter.com/nghuuphuoc 8 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 9 | * @license MIT 10 | */ 11 | 12 | 13 | .bootstrap-validator-form .help-block{margin-bottom:0} -------------------------------------------------------------------------------- /www/public/css/custom.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | .custom { 4 | width: 100px !important; 5 | }.custom1 { 6 | width: 160px !important; 7 | } 8 | 9 | .navbar-fixed-top .nav { 10 | padding: 0px 0; 11 | } 12 | .navbar-nav > li > a, .navbar-brand { 13 | height: 20px; 14 | } 15 | .navbar {min-height:28px !important;} 16 | @media(min-width:768px) { 17 | 18 | .navbar-fixed-top .navbar-brand { 19 | padding: 11px 0; 20 | } 21 | } 22 | .table tbody>tr>td.vert-align{ 23 | vertical-align: middle; 24 | } 25 | .sonos { 26 | height: 48px; 27 | width: 48px; 28 | } 29 | 30 | .body-blue { 31 | background: #eaecfa; 32 | } 33 | 34 | /* Paste this css to your style sheet file or under head tag */ 35 | /* This only works with JavaScript, 36 | if it's not present, don't show loader */ 37 | .no-js #loader { display: none; } 38 | .js #loader { display: block; position: absolute; left: 100px; top: 100; } 39 | .se-pre-con { 40 | position: fixed; 41 | left: 0px; 42 | top: 110px; 43 | width: 100%; 44 | height: 20%; 45 | z-index: 9999; 46 | background: url(../img/Preloader.gif) center no-repeat #fff; 47 | } 48 | -------------------------------------------------------------------------------- /www/public/css/custombootstrap.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | body { 4 | padding-top: 10px; 5 | margin-top: 5px;} 6 | .center1 { 7 | float: none; 8 | margin-left: auto; 9 | margin-right: auto; 10 | } 11 | .custom { 12 | width: 85px !important; 13 | } 14 | .custom1 { 15 | width: 120px !important; 16 | } 17 | .btn-customdelete { 18 | background-color: hsl(0, 0%, 66%) !important; 19 | background-repeat: repeat-x; 20 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#eaeaea", endColorstr="#a8a8a8"); 21 | background-image: -khtml-gradient(linear, left top, left bottom, from(#eaeaea), to(#a8a8a8)); 22 | background-image: -moz-linear-gradient(top, #eaeaea, #a8a8a8); 23 | background-image: -ms-linear-gradient(top, #eaeaea, #a8a8a8); 24 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eaeaea), color-stop(100%, #a8a8a8)); 25 | background-image: -webkit-linear-gradient(top, #eaeaea, #a8a8a8); 26 | background-image: -o-linear-gradient(top, #eaeaea, #a8a8a8); 27 | background-image: linear-gradient(#eaeaea, #a8a8a8); 28 | border-color: #a8a8a8 #a8a8a8 hsl(0, 0%, 59.5%); 29 | color: #333 !important; 30 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.42); 31 | -webkit-font-smoothing: antialiased; 32 | } 33 | .btn-customedit { 34 | background-color: hsl(201, 51%, 79%) !important; 35 | background-repeat: repeat-x; 36 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#fbfcfd", endColorstr="#aed1e4"); 37 | background-image: -khtml-gradient(linear, left top, left bottom, from(#fbfcfd), to(#aed1e4)); 38 | background-image: -moz-linear-gradient(top, #fbfcfd, #aed1e4); 39 | background-image: -ms-linear-gradient(top, #fbfcfd, #aed1e4); 40 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fbfcfd), color-stop(100%, #aed1e4)); 41 | background-image: -webkit-linear-gradient(top, #fbfcfd, #aed1e4); 42 | background-image: -o-linear-gradient(top, #fbfcfd, #aed1e4); 43 | background-image: linear-gradient(#fbfcfd, #aed1e4); 44 | border-color: #aed1e4 #aed1e4 hsl(201, 51%, 74%); 45 | color: #333 !important; 46 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.33); 47 | -webkit-font-smoothing: antialiased; 48 | } 49 | 50 | .btn-customred { 51 | background-color: hsl(0, 69%, 22%) !important; 52 | background-repeat: repeat-x; 53 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#b42121", endColorstr="#5e1111"); 54 | background-image: -khtml-gradient(linear, left top, left bottom, from(#b42121), to(#5e1111)); 55 | background-image: -moz-linear-gradient(top, #b42121, #5e1111); 56 | background-image: -ms-linear-gradient(top, #b42121, #5e1111); 57 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #b42121), color-stop(100%, #5e1111)); 58 | background-image: -webkit-linear-gradient(top, #b42121, #5e1111); 59 | background-image: -o-linear-gradient(top, #b42121, #5e1111); 60 | background-image: linear-gradient(#b42121, #5e1111); 61 | border-color: #5e1111 #5e1111 hsl(0, 69%, 17%); 62 | color: #fff !important; 63 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); 64 | -webkit-font-smoothing: antialiased; 65 | } 66 | 67 | ul.nav li.dropdownsuper:hover ul.supermenu{ 68 | display: block; 69 | margin-top:0px 70 | } 71 | ul.nav li.dropdownsub:hover ul.submenu{ 72 | display: block; 73 | margin-top:0px 74 | } 75 | 76 | .navbar-default { 77 | background-color: #487abb; 78 | border-color: #4273b2; 79 | } 80 | .navbar-default .navbar-brand { 81 | color: #ecf0f1; 82 | } 83 | .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { 84 | color: #d1ffed; 85 | } 86 | .navbar-default .navbar-text { 87 | color: #ecf0f1; 88 | } 89 | .navbar-default .navbar-nav > li > a { 90 | color: #ecf0f1; 91 | } 92 | .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { 93 | color: #d1ffed; 94 | } 95 | .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { 96 | color: #d1ffed; 97 | background-color: #4273b2; 98 | /*background-color: #487abb;*/ 99 | } 100 | .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { 101 | color: #d1ffed; 102 | background-color: #4273b2; 103 | } 104 | .navbar-default .navbar-toggle { 105 | border-color: #4273b2; 106 | } 107 | .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { 108 | background-color: #4273b2; 109 | } 110 | .navbar-default .navbar-toggle .icon-bar { 111 | background-color: #ecf0f1; 112 | } 113 | .navbar-default .navbar-collapse, 114 | .navbar-default .navbar-form { 115 | border-color: #ecf0f1; 116 | } 117 | .navbar-default .navbar-link { 118 | color: #ecf0f1; 119 | } 120 | .navbar-default .navbar-link:hover { 121 | color: #d1ffed; 122 | } 123 | 124 | @media (max-width: 767px) { 125 | .navbar-default .navbar-nav .open .dropdown-menu > li > a { 126 | color: #ecf0f1; 127 | } 128 | .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { 129 | color: #d1ffed; 130 | } 131 | .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { 132 | color: #d1ffed; 133 | background-color: #4273b2; 134 | } 135 | } -------------------------------------------------------------------------------- /www/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /www/public/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /www/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /www/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /www/public/img/Preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/Preloader.gif -------------------------------------------------------------------------------- /www/public/img/Preloader1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/Preloader1.gif -------------------------------------------------------------------------------- /www/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/logo.png -------------------------------------------------------------------------------- /www/public/img/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/pause.png -------------------------------------------------------------------------------- /www/public/img/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/play.png -------------------------------------------------------------------------------- /www/public/img/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homematic-community/hm-sonos/ce1d6d7a240feb38e9015364def159e1da747e80/www/public/img/stop.png -------------------------------------------------------------------------------- /www/public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /www/public/js/bootstrapValidator.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * 4 | * A jQuery plugin to validate form fields. Use with Bootstrap 3 5 | * 6 | * @version v0.4.2 7 | * @author https://twitter.com/nghuuphuoc 8 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 9 | * @license MIT 10 | */ 11 | 12 | !function(a){var b=function(c,d){this.$form=a(c),this.options=a.extend({},b.DEFAULT_OPTIONS,d),this.$invalidField=null,this.$submitButton=null,this.STATUS_NOT_VALIDATED="NOT_VALIDATED",this.STATUS_VALIDATING="VALIDATING",this.STATUS_INVALID="INVALID",this.STATUS_VALID="VALID";var e=document.createElement("div");this._changeEvent="oninput"in e?"input":"keydown",this._submitIfValid=null,this._init()};b.DEFAULT_OPTIONS={elementClass:"bootstrap-validator-form",message:"This value is not valid",excluded:[":disabled",":hidden",":not(:visible)"],feedbackIcons:{valid:null,invalid:null,validating:null},submitButtons:'button[type="submit"]',submitHandler:null,live:"enabled",fields:null},b.prototype={constructor:b,_init:function(){var b,c,d,e,f,g,h,i=this,j={excluded:this.$form.attr("data-bv-excluded"),trigger:this.$form.attr("data-bv-trigger"),message:this.$form.attr("data-bv-message"),submitButtons:this.$form.attr("data-bv-submitbuttons"),live:this.$form.attr("data-bv-live"),fields:{},feedbackIcons:{valid:this.$form.attr("data-bv-feedbackicons-valid"),invalid:this.$form.attr("data-bv-feedbackicons-invalid"),validating:this.$form.attr("data-bv-feedbackicons-validating")}};this.$form.attr("novalidate","novalidate").addClass(this.options.elementClass).on("submit.bv",function(a){a.preventDefault(),i.validate()}).on("click",this.options.submitButtons,function(){i.$submitButton=a(this),i._submitIfValid=!0}).find("[name], [data-bv-field]").each(function(){var i=a(this);if("hidden"!=i.attr("type")){var k=i.attr("name")||i.attr("data-bv-field");i.attr("data-bv-field",k),j.fields[k]=a.extend({},{trigger:i.attr("data-bv-trigger"),message:i.attr("data-bv-message"),container:i.attr("data-bv-container"),selector:i.attr("data-bv-selector"),validators:{}},j.fields[k]);for(c in a.fn.bootstrapValidator.validators)if(b=a.fn.bootstrapValidator.validators[c],d=i.attr("data-bv-"+c.toLowerCase())+"",h="function"==typeof b.enableByHtml5?b.enableByHtml5(a(this)):null,h&&"false"!=d||h!==!0&&(""==d||"true"==d)){b.html5Attributes=b.html5Attributes||{message:"message"},j.fields[k].validators[c]=a.extend({},1==h?{}:h,j.fields[k].validators[c]);for(g in b.html5Attributes)e=b.html5Attributes[g],f=i.attr("data-bv-"+c.toLowerCase()+"-"+g),f&&("true"==f?f=!0:"false"==f&&(f=!1),j.fields[k].validators[c][e]=f)}}}),this.options=a.extend(!0,this.options,j),"string"==typeof this.options.excluded&&(this.options.excluded=a.map(this.options.excluded.split(","),function(a){return a.trim()}));for(var k in this.options.fields)this._initField(k);this.setLiveMode(this.options.live)},_initField:function(b){if(null!=this.options.fields[b]&&null!=this.options.fields[b].validators){var c=this.getFieldElements(b);if(null==c)return void delete this.options.fields[b];for(var d in this.options.fields[b].validators)a.fn.bootstrapValidator.validators[d]||delete this.options.fields[b].validators[d];for(var e=this,f=c.attr("type"),g="radio"==f||"checkbox"==f||"file"==f||"SELECT"==c[0].tagName?"change":e._changeEvent,h=c.length,i=1==h||"radio"==f||"checkbox"==f,j=0;h>j;j++){var k=a(c[j]),l=k.parents(".form-group"),m=this.options.fields[b].container?l.find(this.options.fields[b].container):this._getMessageContainer(k);k.attr("data-bv-field")||k.attr("data-bv-field",b),k.on(g+".update.bv",function(){e._submitIfValid=!1,i?e.updateStatus(b,e.STATUS_NOT_VALIDATED,null):e.updateElementStatus(a(this),e.STATUS_NOT_VALIDATED,null)}),k.data("bv.messages",m);for(d in this.options.fields[b].validators)k.data("bv.result."+d,this.STATUS_NOT_VALIDATED),i&&j!=h-1||a("").css("display","none").attr("data-bv-validator",d).html(this.options.fields[b].validators[d].message||this.options.fields[b].message||this.options.message).addClass("help-block").appendTo(m);if(this.options.feedbackIcons&&this.options.feedbackIcons.validating&&this.options.feedbackIcons.invalid&&this.options.feedbackIcons.valid&&(!i||j==h-1)){l.addClass("has-feedback");var n=a("").css("display","none").addClass("form-control-feedback").attr("data-bv-field",b).insertAfter(k);0==l.find("label").length&&n.css("top",0)}}null==this.options.fields[b].enabled&&(this.options.fields[b].enabled=!0)}},_getMessageContainer:function(a){var b=a.parent();if(b.hasClass("form-group"))return b;var c=b.attr("class");if(!c)return this._getMessageContainer(b);c=c.split(" ");for(var d=c.length,e=0;d>e;e++)if(/^col-(xs|sm|md|lg)-\d+$/.test(c[e])||/^col-(xs|sm|md|lg)-offset-\d+$/.test(c[e]))return b;return this._getMessageContainer(b)},_submit:function(){return this.isValid()?void(this.options.submitHandler&&"function"==typeof this.options.submitHandler?this.options.submitHandler.call(this,this,this.$form,this.$submitButton):this.disableSubmitButtons(!0).defaultSubmit()):("submitted"==this.options.live&&this.setLiveMode("enabled"),void(this.$invalidField&&this.$invalidField.focus()))},_isExcluded:function(a){if(this.options.excluded)for(var b in this.options.excluded)if("string"==typeof this.options.excluded[b]&&a.is(this.options.excluded[b])||"function"==typeof this.options.excluded[b]&&1==this.options.excluded[b].call(this,a,this))return!0;return!1},getFieldElements:function(b){var c=this.options.fields[b].selector?a(this.options.fields[b].selector):this.$form.find('[name="'+b+'"]');return 0==c.length?null:c},setLiveMode:function(b){if(this.options.live=b,"submitted"==b)return this;var c=this;for(var d in this.options.fields)!function(e){var f=c.getFieldElements(e);if(f)for(var g=f.attr("type"),h=f.length,i=1==h||"radio"==g||"checkbox"==g,j=c.options.fields[d].trigger||c.options.trigger||("radio"==g||"checkbox"==g||"file"==g||"SELECT"==f[0].tagName?"change":c._changeEvent),k=a.map(j.split(" "),function(a){return a+".live.bv"}).join(" "),l=0;h>l;l++)"enabled"==b?a(f[l]).on(k,function(){i?c.validateField(e):c.validateFieldElement(a(this),!1)}):a(f[l]).off(k)}(d);return this},disableSubmitButtons:function(a){return a?"disabled"!=this.options.live&&this.$form.find(this.options.submitButtons).attr("disabled","disabled"):this.$form.find(this.options.submitButtons).removeAttr("disabled"),this},validate:function(){if(!this.options.fields)return this;this.disableSubmitButtons(!0);for(var a in this.options.fields)this.validateField(a);return this.$submitButton&&this._submit(),this},validateField:function(b){for(var c=this.getFieldElements(b),d=c.attr("type"),e="radio"==d||"checkbox"==d?1:c.length,f=0;e>f;f++)this.validateFieldElement(a(c[f]),1==e);return this},validateFieldElement:function(b,c){var d,e,f=this,g=b.attr("data-bv-field"),h=this.options.fields[g].validators;if(!this.options.fields[g].enabled||this._isExcluded(b))return this;for(d in h){b.data("bv.dfs."+d)&&b.data("bv.dfs."+d).reject();var i=b.data("bv.result."+d);i!=this.STATUS_VALID&&i!=this.STATUS_INVALID&&(b.data("bv.result."+d,this.STATUS_VALIDATING),e=a.fn.bootstrapValidator.validators[d].validate(this,b,h[d]),"object"==typeof e?(c?this.updateStatus(g,this.STATUS_VALIDATING,d):this.updateElementStatus(b,this.STATUS_VALIDATING,d),b.data("bv.dfs."+d,e),e.done(function(a,b,d){a.removeData("bv.dfs."+b),c?f.updateStatus(a.attr("data-bv-field"),d?f.STATUS_VALID:f.STATUS_INVALID,b):f.updateElementStatus(a,d?f.STATUS_VALID:f.STATUS_INVALID,b),d&&1==f._submitIfValid&&f._submit()})):"boolean"==typeof e&&(c?this.updateStatus(g,e?this.STATUS_VALID:this.STATUS_INVALID,d):this.updateElementStatus(b,e?this.STATUS_VALID:this.STATUS_INVALID,d)))}return this},updateStatus:function(b,c,d){for(var e=this.getFieldElements(b),f=e.attr("type"),g="radio"==f||"checkbox"==f?1:e.length,h=0;g>h;h++)this.updateElementStatus(a(e[h]),c,d);return this},updateElementStatus:function(b,c,d){var e=this,f=b.attr("data-bv-field"),g=b.parents(".form-group"),h=b.data("bv.messages"),i=g.find(".help-block[data-bv-validator]"),j=h.find(".help-block[data-bv-validator]"),k=g.find('.form-control-feedback[data-bv-field="'+f+'"]');if(d)b.data("bv.result."+d,c);else for(var l in this.options.fields[f].validators)b.data("bv.result."+l,c);switch(c){case this.STATUS_VALIDATING:this.disableSubmitButtons(!0),g.removeClass("has-success").removeClass("has-error"),d?j.filter('.help-block[data-bv-validator="'+d+'"]').hide():j.hide(),k&&k.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).addClass(this.options.feedbackIcons.validating).show();break;case this.STATUS_INVALID:this.disableSubmitButtons(!0),g.removeClass("has-success").addClass("has-error"),d?j.filter('[data-bv-validator="'+d+'"]').show():j.show(),k&&k.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.validating).addClass(this.options.feedbackIcons.invalid).show();break;case this.STATUS_VALID:d?j.filter('[data-bv-validator="'+d+'"]').hide():j.hide();var m=0==j.filter(function(){var c=a(this).css("display"),d=a(this).attr("data-bv-validator");return"block"==c||b.data("bv.result."+d)!=e.STATUS_VALID}).length;this.disableSubmitButtons(m?!1:!0),k&&k.removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).removeClass(this.options.feedbackIcons.valid).addClass(m?this.options.feedbackIcons.valid:this.options.feedbackIcons.invalid).show();var n=0==i.filter(function(){var c=a(this).css("display"),d=a(this).attr("data-bv-validator");return"block"==c||b.data("bv.result."+d)!=e.STATUS_VALID}).length;g.removeClass("has-error has-success").addClass(n?"has-success":"has-error");break;case this.STATUS_NOT_VALIDATED:default:this.disableSubmitButtons(!1),g.removeClass("has-success").removeClass("has-error"),d?j.filter('.help-block[data-bv-validator="'+d+'"]').hide():j.hide(),k&&k.removeClass(this.options.feedbackIcons.valid).removeClass(this.options.feedbackIcons.invalid).removeClass(this.options.feedbackIcons.validating).hide()}return this},isValid:function(){var b,c,d,e,f,g,h,i;for(c in this.options.fields)if(null!=this.options.fields[c]&&this.options.fields[c].enabled)for(b=this.getFieldElements(c),e=b.attr("type"),h="radio"==e||"checkbox"==e?1:b.length,i=0;h>i;i++)if(d=a(b[i]),!this._isExcluded(d))for(g in this.options.fields[c].validators){if(f=d.data("bv.result."+g),f==this.STATUS_NOT_VALIDATED||f==this.STATUS_VALIDATING)return!1;if(f==this.STATUS_INVALID)return this.$invalidField=d,!1}return!0},defaultSubmit:function(){this.$form.off("submit.bv").submit()},resetForm:function(b){var c,d,e,f,g;for(c in this.options.fields){d=this.getFieldElements(c),e=d.length;for(var h=0;e>h;h++)for(g in this.options.fields[c].validators)a(d[h]).removeData("bv.dfs."+g);this.updateStatus(c,this.STATUS_NOT_VALIDATED,null),b&&(f=d.attr("type"),"radio"==f||"checkbox"==f?d.removeAttr("checked").removeAttr("selected"):d.val(""))}return this.$invalidField=null,this.$submitButton=null,this.disableSubmitButtons(!1),this},enableFieldValidators:function(a,b){return this.options.fields[a].enabled=b,this.updateStatus(a,this.STATUS_NOT_VALIDATED,null),this}},a.fn.bootstrapValidator=function(c){return this.each(function(){var d=a(this),e=d.data("bootstrapValidator");e||d.data("bootstrapValidator",e=new b(this,c)),"string"==typeof c&&e[c]()})},a.fn.bootstrapValidator.validators={},a.fn.bootstrapValidator.Constructor=b}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.base64={validate:function(a,b){var c=b.val();return""==c?!0:/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.between={html5Attributes:{message:"message",min:"min",max:"max",inclusive:"inclusive"},enableByHtml5:function(a){return"range"==a.attr("type")?{min:a.attr("min"),max:a.attr("max")}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!0?d>c.min&&d=c.min&&d<=c.max)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.callback={validate:function(b,c,d){var e=c.val();if(d.callback&&"function"==typeof d.callback){var f=new a.Deferred;return f.resolve(c,"callback",d.callback.call(this,e,b)),f}return!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.choice={html5Attributes:{message:"message",min:"min",max:"max"},validate:function(a,b,c){var d=a.getFieldElements(b.attr("data-bv-field")).filter(":checked").length;return c.min&&dc.max?!1:!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.creditCard={validate:function(a,b){var c=b.val();if(""==c)return!0;if(/[^0-9-\s]+/.test(c))return!1;c=c.replace(/\D/g,"");for(var d=0,e=0,f=!1,g=c.length,h=g-1;h>=0;h--)e=parseInt(c.charAt(h),10),f&&(e*=2)>9&&(e-=9),d+=e,f=!f;if(d%10!=0)return!1;var i,j,k={AMERICAN_EXPRESS:{length:[15],prefix:["34","37"]},DINERS_CLUB:{length:[14],prefix:["300","301","302","303","304","305","36"]},DINERS_CLUB_US:{length:[16],prefix:["54","55"]},DISCOVER:{length:[16],prefix:["6011","622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925","644","645","646","647","648","649","65"]},JCB:{length:[16],prefix:["3528","3529","353","354","355","356","357","358"]},LASER:{length:[16,17,18,19],prefix:["3528","3529","353","354","355","356","357","358"]},MAESTRO:{length:[12,13,14,15,16,17,18,19],prefix:["5018","5020","5038","6304","6759","6761","6762","6763","6764","6765","6766"]},MASTERCARD:{length:[16],prefix:["51","52","53","54","55"]},SOLO:{length:[16,18,19],prefix:["6334","6767"]},UNIONPAY:{length:[16,17,18,19],prefix:["622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925"]},VISA:{length:[16],prefix:["4"]}};for(i in k)for(j in k[i].prefix)if(c.substr(0,k[i].prefix[j].length)==k[i].prefix[j]&&-1!=k[i].length.indexOf(c.length))return!0;return!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.cvv={html5Attributes:{message:"message",ccfield:"creditCardField"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;if(!/^[0-9]{3,4}$/.test(d))return!1;if(!c.creditCardField)return!0;var e=a.getFieldElements(c.creditCardField).val();if(""==e)return!0;var f,g,h={AMERICAN_EXPRESS:{length:[15],prefix:["34","37"]},DINERS_CLUB:{length:[14],prefix:["300","301","302","303","304","305","36"]},DINERS_CLUB_US:{length:[16],prefix:["54","55"]},DISCOVER:{length:[16],prefix:["6011","622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925","644","645","646","647","648","649","65"]},JCB:{length:[16],prefix:["3528","3529","353","354","355","356","357","358"]},LASER:{length:[16,17,18,19],prefix:["3528","3529","353","354","355","356","357","358"]},MAESTRO:{length:[12,13,14,15,16,17,18,19],prefix:["5018","5020","5038","6304","6759","6761","6762","6763","6764","6765","6766"]},MASTERCARD:{length:[16],prefix:["51","52","53","54","55"]},SOLO:{length:[16,18,19],prefix:["6334","6767"]},UNIONPAY:{length:[16,17,18,19],prefix:["622126","622127","622128","622129","62213","62214","62215","62216","62217","62218","62219","6222","6223","6224","6225","6226","6227","6228","62290","62291","622920","622921","622922","622923","622924","622925"]},VISA:{length:[16],prefix:["4"]}},i=null;for(f in h)for(g in h[f].prefix)if(e.substr(0,h[f].prefix[g].length)==h[f].prefix[g]&&-1!=h[f].length.indexOf(e.length)){i=f;break}return null==i?!1:"AMERICAN_EXPRESS"==i?4==d.length:3==d.length}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.date={html5Attributes:{message:"message",format:"format"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;c.format=c.format||"MM/DD/YYYY";var e=-1!=c.format.indexOf("/")?"/":-1!=c.format.indexOf("-")?"-":null;if(null==e)return!1;var f,g,h,i,j=null,k=null;switch(!0){case"/"==e&&(i=d.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/i))&&"YYYY/DD/MM"==c.format:case"-"==e&&(i=d.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/i))&&"YYYY-DD-MM"==c.format:h=i[1],g=i[2],f=i[3];break;case"/"==e&&(i=d.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/i))&&"DD/MM/YYYY"==c.format:case"-"==e&&(i=d.match(/^(\d{1,2})-(\d{1,2})-(\d{4})$/i))&&"DD-MM-YYYY"==c.format:g=i[1],f=i[2],h=i[3];break;case"/"==e&&(i=d.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/i))&&"YYYY/MM/DD"==c.format:case"-"==e&&(i=d.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/i))&&"YYYY-MM-DD"==c.format:h=i[1],f=i[2],g=i[3];break;case"/"==e&&(i=d.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/i))&&"MM/DD/YYYY"==c.format:case"-"==e&&(i=d.match(/^(\d{1,2})-(\d{1,2})-(\d{4})$/i))&&"MM-DD-YYYY"==c.format:f=i[1],g=i[2],h=i[3];break;case"/"==e&&(i=d.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"YYYY/DD/MM h:m A"==c.format:case"-"==e&&(i=d.match(/^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"YYYY-DD-MM h:m A"==c.format:h=i[1],g=i[2],f=i[3],k=i[4],j=i[5];break;case"/"==e&&(i=d.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"DD/MM/YYYY h:m A"==c.format:case"-"==e&&(i=d.match(/^(\d{1,2})-(\d{1,2})-(\d{4})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"DD-MM-YYYY h:m A"==c.format:g=i[1],f=i[2],h=i[3],k=i[4],j=i[5];break;case"/"==e&&(i=d.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"YYYY/MM/DD h:m A"==c.format:case"-"==e&&(i=d.match(/^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"YYYY-MM-DD h:m A"==c.format:h=i[1],f=i[2],g=i[3],k=i[4],j=i[5];break;case"/"==e&&(i=d.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"MM/DD/YYYY h:m A"==c.format:case"-"==e&&(i=d.match(/^(\d{1,2})-(\d{1,2})-(\d{4})\s+(\d{1,2}):(\d{1,2})\s+(AM|PM)$/i))&&"MM-DD-YYYY h:m A"==c.format:f=i[1],g=i[2],h=i[3],k=i[4],j=i[5];break;default:return!1}if(k&&j&&(k=parseInt(k,10),j=parseInt(j,10),1>k||k>12||0>j||j>59))return!1;if(g=parseInt(g,10),f=parseInt(f,10),h=parseInt(h,10),1e3>h||h>9999||0==f||f>12)return!1;var l=[31,28,31,30,31,30,31,31,30,31,30,31];return(h%400==0||h%100!=0&&h%4==0)&&(l[1]=29),g>0&&g<=l[f-1]}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.different={html5Attributes:{message:"message",field:"field"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=a.getFieldElements(c.field);return null==e?!0:d!=e.val()?(a.updateStatus(c.field,a.STATUS_VALID,"different"),!0):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.digits={validate:function(a,b){var c=b.val();return""==c?!0:/^\d+$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.emailAddress={enableByHtml5:function(a){return"email"==a.attr("type")},validate:function(a,b){var c=b.val();if(""==c)return!0;var d=/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;return d.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.greaterThan={html5Attributes:{message:"message",value:"value",inclusive:"inclusive"},enableByHtml5:function(a){var b=a.attr("min");return b?{value:b}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!0?d>c.value:d>=c.value)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.hex={validate:function(a,b){var c=b.val();return""==c?!0:/^[0-9a-fA-F]+$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.hexColor={enableByHtml5:function(a){return"color"==a.attr("type")},validate:function(a,b){var c=b.val();return""==c?!0:/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.iban={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e={AD:"AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Z0-9]{12}",AE:"AE[0-9]{2}[0-9]{3}[0-9]{16}",AL:"AL[0-9]{2}[0-9]{8}[A-Z0-9]{16}",AO:"AO[0-9]{2}[0-9]{21}",AT:"AT[0-9]{2}[0-9]{5}[0-9]{11}",AZ:"AZ[0-9]{2}[A-Z]{4}[A-Z0-9]{20}",BA:"BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}",BE:"BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}",BF:"BF[0-9]{2}[0-9]{23}",BG:"BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}",BH:"BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}",BI:"BI[0-9]{2}[0-9]{12}",BJ:"BJ[0-9]{2}[A-Z]{1}[0-9]{23}",BR:"BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z][A-Z0-9]",CH:"CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}",CI:"CI[0-9]{2}[A-Z]{1}[0-9]{23}",CM:"CM[0-9]{2}[0-9]{23}",CR:"CR[0-9]{2}[0-9]{3}[0-9]{14}",CV:"CV[0-9]{2}[0-9]{21}",CY:"CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}",CZ:"CZ[0-9]{2}[0-9]{20}",DE:"DE[0-9]{2}[0-9]{8}[0-9]{10}",DK:"DK[0-9]{2}[0-9]{14}",DO:"DO[0-9]{2}[A-Z0-9]{4}[0-9]{20}",DZ:"DZ[0-9]{2}[0-9]{20}",EE:"EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}",ES:"ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}",FI:"FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}",FO:"FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}",FR:"FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}",GB:"GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}",GE:"GE[0-9]{2}[A-Z]{2}[0-9]{16}",GI:"GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}",GL:"GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}",GR:"GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Z0-9]{16}",GT:"GT[0-9]{2}[A-Z0-9]{4}[A-Z0-9]{20}",HR:"HR[0-9]{2}[0-9]{7}[0-9]{10}",HU:"HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}",IE:"IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}",IL:"IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}",IR:"IR[0-9]{2}[0-9]{22}",IS:"IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}",IT:"IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}",JO:"JO[0-9]{2}[A-Z]{4}[0-9]{4}[0]{8}[A-Z0-9]{10}",KW:"KW[0-9]{2}[A-Z]{4}[0-9]{22}",KZ:"KZ[0-9]{2}[0-9]{3}[A-Z0-9]{13}",LB:"LB[0-9]{2}[0-9]{4}[A-Z0-9]{20}",LI:"LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}",LT:"LT[0-9]{2}[0-9]{5}[0-9]{11}",LU:"LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}",LV:"LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}",MC:"MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}",MD:"MD[0-9]{2}[A-Z0-9]{20}",ME:"ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",MG:"MG[0-9]{2}[0-9]{23}",MK:"MK[0-9]{2}[0-9]{3}[A-Z0-9]{10}[0-9]{2}",ML:"ML[0-9]{2}[A-Z]{1}[0-9]{23}",MR:"MR13[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}",MT:"MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}",MU:"MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}",MZ:"MZ[0-9]{2}[0-9]{21}",NL:"NL[0-9]{2}[A-Z]{4}[0-9]{10}",NO:"NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}",PK:"PK[0-9]{2}[A-Z]{4}[A-Z0-9]{16}",PL:"PL[0-9]{2}[0-9]{8}[0-9]{16}",PS:"PS[0-9]{2}[A-Z]{4}[A-Z0-9]{21}",PT:"PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}",QA:"QA[0-9]{2}[A-Z]{4}[A-Z0-9]{21}",RO:"RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}",RS:"RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",SA:"SA[0-9]{2}[0-9]{2}[A-Z0-9]{18}",SE:"SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}",SI:"SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}",SK:"SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}",SM:"SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Z0-9]{12}",SN:"SN[0-9]{2}[A-Z]{1}[0-9]{23}",TN:"TN59[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}",TR:"TR[0-9]{2}[0-9]{5}[A-Z0-9]{1}[A-Z0-9]{16}",VG:"VG[0-9]{2}[A-Z]{4}[0-9]{16}"};d=d.replace(/[^a-zA-Z0-9]/g,"").toUpperCase();var f=c.country||d.substr(0,2);if(!e[f])return!1;if(!new RegExp("^"+e[f]+"$").test(d))return!1;d=d.substr(4)+d.substr(0,4),d=d.split("").map(function(a){var b=a.charCodeAt(0);return b>="A".charCodeAt(0)&&b<="Z".charCodeAt(0)?b-"A".charCodeAt(0)+10:a}).join("");for(var g=parseInt(d.substr(0,1),10),h=d.length,i=1;h>i;++i)g=(10*g+parseInt(d.substr(i,1),10))%97;return 1==g}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.identical={html5Attributes:{message:"message",field:"field"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=a.getFieldElements(c.field);return null==e?!0:d==e.val()?(a.updateStatus(c.field,a.STATUS_VALID,"identical"),!0):!1}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.integer={enableByHtml5:function(a){return"number"==a.attr("type")},validate:function(a,b){var c=b.val();return""==c?!0:/^(?:-?(?:0|[1-9][0-9]*))$/.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.ip={html5Attributes:{message:"message",ipv4:"ipv4",ipv6:"ipv6"},validate:function(b,c,d){var e=c.val();return""==e?!0:(d=a.extend({},{ipv4:!0,ipv6:!0},d),d.ipv4?/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e):d.ipv6?/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/.test(str):!1)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.isbn={validate:function(a,b){var c=b.val();if(""==c)return!0;c=c.replace(/[^\dX]/gi,"");var d,e=c.split(""),f=0;switch(e.length){case 10:f=0;for(var g=0;9>g;g++)f+=(10-g)*parseInt(e[g]);return d=11-f%11,11==d?d=0:10==d&&(d="X"),d==e[9];case 13:f=0;for(var g=0;12>g;g++)f+=g%2==0?parseInt(e[g]):3*parseInt(e[g]);return d=10-f%10,10==d&&(d="0"),d==e[12];default:return!1}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.lessThan={html5Attributes:{message:"message",value:"value",inclusive:"inclusive"},enableByHtml5:function(a){var b=a.attr("max");return b?{value:b}:!1},validate:function(a,b,c){var d=b.val();return""==d?!0:(d=parseFloat(d),c.inclusive===!1?d<=c.value:d0:""!=a.trim(c.val())}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.numeric={validate:function(a,b){var c=b.val();return""==c?!0:!isNaN(parseFloat(c))&&isFinite(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.phone={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=(c.country||"US").toUpperCase();switch(e){case"US":default:return d=d.replace(/\D/g,""),/^(?:(1\-?)|(\+1 ?))?\(?(\d{3})[\)\-\.]?(\d{3})[\-\.]?(\d{4})$/.test(d)&&10==d.length}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.regexp={html5Attributes:{message:"message",regexp:"regexp"},enableByHtml5:function(a){var b=a.attr("pattern");return b?{regexp:b}:!1},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e="string"==typeof c.regexp?new RegExp(c.regexp):c.regexp;return e.test(d)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.remote={html5Attributes:{message:"message",url:"url",name:"name"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;var f=c.attr("data-bv-field"),g=d.data;null==g&&(g={}),"function"==typeof g&&(g=g.call(this,b)),g[d.name||f]=e;var h=new a.Deferred,i=a.ajax({type:"POST",url:d.url,dataType:"json",data:g});return i.then(function(a){h.resolve(c,"remote",a.valid===!0||"true"===a.valid)}),h.fail(function(){i.abort()}),h}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.siren={validate:function(a,b){var c=b.val();if(""==c)return!0;for(var d,e=0,f=c.length,g=0;f>g;g++)d=parseInt(c.charAt(g)),g%2==1&&(d=2*d,d>9&&(d-=9)),e+=d;return e%10==0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.siret={validate:function(a,b){var c=b.val();if(""==c)return!0;for(var d,e=0,f=c.length,g=0;f>g;g++)d=parseInt(c.charAt(g)),g%2==0&&(d=2*d,d>9&&(d-=9)),e+=d;return e%10==0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.step={html5Attributes:{message:"message",base:"baseValue",step:"step"},validate:function(b,c,d){var e=c.val();if(""==e)return!0;if(d=a.extend({},{baseValue:0,step:1},d),e=parseFloat(e),isNaN(e)||!isFinite(e))return!1;var f=function(a,b){var c=Math.pow(10,b);a*=c;var d=a>0|-(0>a),e=a%1===.5*d;return e?(Math.floor(a)+(d>0))/c:Math.round(a)/c},g=function(a,b){if(0==b)return 1;var c=(a+"").split("."),d=(b+"").split("."),e=(1==c.length?0:c[1].length)+(1==d.length?0:d[1].length);return f(a-b*Math.floor(a/b),e)},h=g(e-d.baseValue,d.step);return 0==h||h==d.step}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.stringCase={html5Attributes:{message:"message","case":"case"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e=(c["case"]||"lower").toLowerCase();switch(e){case"upper":return d===d.toUpperCase();case"lower":default:return d===d.toLowerCase()}}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.stringLength={html5Attributes:{message:"message",min:"min",max:"max"},enableByHtml5:function(a){var b=a.attr("maxlength");return b?{max:parseInt(b,10)}:!1},validate:function(b,c,d){var e=c.val();if(""==e)return!0;var f=a.trim(e).length;return d.min&&fd.max?!1:!0}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.uri={enableByHtml5:function(a){return"url"==a.attr("type")},validate:function(a,b){var c=b.val();if(""==c)return!0;var d=new RegExp("^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:/[^\\s]*)?$","i");return d.test(c)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.uuid={html5Attributes:{message:"message",version:"version"},validate:function(a,b,c){var d=b.val();if(""==d)return!0;var e={3:/^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,4:/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,5:/^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,all:/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i},f=c.version?c.version+"":"all";return null==e[f]?!0:e[f].test(d)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.vin={validate:function(a,b){var c=b.val();if(""==c)return!0;if(!/^[a-hj-npr-z0-9]{8}[0-9xX][a-hj-npr-z0-9]{8}$/i.test(c))return!1;c=c.toUpperCase();for(var d={A:1,B:2,C:3,D:4,E:5,F:6,G:7,H:8,J:1,K:2,L:3,M:4,N:5,P:7,R:9,S:2,T:3,U:4,V:5,W:6,X:7,Y:8,Z:9,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,0:0},e=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],f=0,g=c.length,h=0;g>h;h++)f+=d[c.charAt(h)+""]*e[h];var i=f%11;return 10==i&&(i="X"),i==c.charAt(8)}}}(window.jQuery),function(a){a.fn.bootstrapValidator.validators.zipCode={html5Attributes:{message:"message",country:"country"},validate:function(a,b,c){var d=b.val();if(""==d||!c.country)return!0;switch(c.country=c.country||"US",c.country.toUpperCase()){case"DK":return/^(DK(-|\s)?)?\d{4}$/i.test(d);case"SE":return/^(S-)?\d{3}\s?\d{2}$/i.test(d);case"US":default:return/^\d{4,5}([\-]\d{4})?$/.test(d)}}}}(window.jQuery); -------------------------------------------------------------------------------- /www/server.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | source [file join [file dirname [info script]] sonos2inc.tcl] ;# Include-File 3 | set info [encoding convertfrom utf-8 [url-decode [ gets stdin ]]] 4 | regexp "(Logtext=.*)" $info dummy info 5 | puts "Content-type:text/html\n" 6 | set append "a" 7 | set Logtext "" 8 | set udp "" 9 | if { $info != "" } { 10 | foreach {line} [split $info "&"] { 11 | switch -glob -- $line { 12 | Logtext* { 13 | regexp "Logtext=(.*)" "$line" dummy Logtext 14 | } 15 | append* { 16 | set append "w" 17 | } 18 | udp* { 19 | set udp 1 20 | } 21 | } 22 | } 23 | } 24 | if { $Logtext != "" } { 25 | log $Logtext $append 26 | } 27 | if { $udp != "" } { 28 | puts -nonewline [Udp 3] 29 | } 30 | -------------------------------------------------------------------------------- /www/settings/ZoneGroupState.txt: -------------------------------------------------------------------------------- 1 | Küche,192.168.1.103,TV-Zimmer,192.168.1.101,Wohnzimmer,192.168.1.105 2 | -------------------------------------------------------------------------------- /www/settings/settings.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | source [file join [file dirname [info script]] ../sonos2inc.tcl] ;# Include-File 3 | 4 | 5 | # read POST 6 | set radio "" 7 | set sonoszone "" 8 | set stdvolume "" 9 | set volumeup "" 10 | set volumedown "" 11 | set messagevolume "" 12 | set messagespeicher "" 13 | set timeout "" 14 | set helpRadio "" 15 | set helpSonoszone "" 16 | set helpStdvolume "" 17 | set helpVolumeup "" 18 | set helpVolumedown "" 19 | set helpMessagevolume "" 20 | set helpMessagespeicher "" 21 | set helpTimeout "" 22 | set message "" 23 | set button "" 24 | set udp "" 25 | set udpbutton "" 26 | set info [encoding convertfrom utf-8 [url-decode [ gets stdin ]]] 27 | regexp "(sonoszone=.*)" $info dummy info 28 | regsub -all "\r" $info {} info ;#" %> 29 | regsub -all {\&} $info "\r" info ;#" %> 30 | if { $info != "" } { 31 | foreach {line} [split $info "\r"] { 32 | switch -glob -- $line { 33 | sonoszone=* { 34 | regexp "sonoszone=(.*)" "$line" dummy sonoszone 35 | } 36 | radio=* { 37 | regexp "radio=(.*)" "$line" dummy radio 38 | } 39 | stdvolume=* { 40 | regexp "stdvolume=(.*)" "$line" dummy stdvolume 41 | } 42 | volumeup=* { 43 | regexp "volumeup=(.*)" "$line" dummy volumeup 44 | } 45 | volumedown=* { 46 | regexp "volumedown=(.*)" "$line" dummy volumedown 47 | } 48 | messagevolume=* { 49 | regexp "messagevolume=(.*)" "$line" dummy messagevolume 50 | } 51 | messagespeicher* { 52 | regexp "messagespeicher=(.*)" "$line" dummy messagespeicher 53 | } 54 | timeout* { 55 | regexp "timeout=(.*)" "$line" dummy timeout 56 | } 57 | resetBtn* { 58 | regexp "resetBtn=(.*)" "$line" dummy button 59 | } 60 | udpbutton* { 61 | regexp "udpbutton=(.*)" "$line" dummy udpbutton 62 | } 63 | udp* { 64 | regexp "udp=(.*)" "$line" dummy udp 65 | } 66 | js_aktiv* { 67 | regexp "js_aktiv=(.*)" "$line" dummy js_aktiv 68 | if { $js_aktiv == "0"} { 69 | set message [getMessage "Sie haben Javascript deaktiviert, deshalb funktionieren manche Formulare nur eingeschränkt! Bitte aktivieren Sie wenn möglich Javascript" warning] 70 | } 71 | } 72 | } 73 | } 74 | if { $button != ""} { 75 | set message "$message [getMessage "Daten neu geladen" info]" 76 | set button "reload" 77 | } elseif { $udp == "1" || $udpbutton == "udp" } { 78 | Cfg::Load 79 | set Cfg::sonoszone [Udp] 80 | Cfg::Save 81 | set message "$message [getMessage "Sonoszonen per Udp geladen" info]" 82 | set button "reload" 83 | } else { 84 | if {$sonoszone != ""} { 85 | if { [string length $sonoszone] < 20 } { 86 | set helpSonoszone "

    Die Sonoszonen müssen mindestens 20 Zeichen lang sein

    " 87 | } 88 | } else { 89 | set helpSonoszone "

    Die Sonoszonen müssen eingegeben werden

    " 90 | } 91 | if {$radio != ""} { 92 | if { [string length $radio] < 5 } { 93 | set helpRadio "

    Die Radioliste sollte mindestens 5 Zeichen lang sein

    " 94 | } 95 | } 96 | if {$messagespeicher == ""} { 97 | set helpMessagespeicher "

    Der Messagepfad muss eingegeben werden

    " 98 | } 99 | if {$stdvolume != ""} { 100 | if [string is integer $stdvolume] { 101 | set stdvolume [ expr round($stdvolume) ] 102 | if { $stdvolume > 100 || $stdvolume < 0 } { 103 | set helpStdvolume "

    Das Standard-Volume muss zwischen 0 und 100 sein

    " 104 | } 105 | } else { 106 | set helpStdvolume "

    Das Standard-Volume muss eine ganze Zahl sein

    " 107 | } 108 | } else { 109 | set helpStdvolume "

    Das Standard-Volume muss eingegeben werden

    " 110 | } 111 | if {$timeout != ""} { 112 | if [string is integer $timeout] { 113 | set timeout [ expr round($timeout) ] 114 | if { $timeout > 10 || $timeout < 1 } { 115 | set helpTimeout "

    Das Timeout muss zwischen 1 und 10 sein

    " 116 | } 117 | } else { 118 | set helpTimeout "

    Das Timeout muss eine ganze Zahl sein

    " 119 | } 120 | } else { 121 | set helpTimeout "

    Das Timeout muss eingegeben werden

    " 122 | } 123 | if {$volumeup != ""} { 124 | if [string is integer $volumeup] { 125 | set volumeup [ expr round($volumeup) ] 126 | if { $volumeup > 20 || $volumeup < 1 } { 127 | set helpVolumeup "

    Das VolumeUp muss zwischen 1 und 20 sein

    " 128 | } 129 | } else { 130 | set helpVolumeup "

    Das VolumeUp muss eine ganze Zahl sein

    " 131 | } 132 | } else { 133 | set helpVolumeup "

    Das VolumeUp muss eingegeben werden

    " 134 | } 135 | if {$volumedown != ""} { 136 | if [string is integer $volumedown] { 137 | set volumedown [ expr round($volumedown) ] 138 | if { $volumedown > 20 || $volumedown < 1 } { 139 | set helpVolumedown "

    Das VolumeDown muss zwischen 1 und 20 sein

    " 140 | } 141 | } else { 142 | set helpVolumedown "

    Das VolumeDown muss eine ganze Zahl sein

    " 143 | } 144 | } else { 145 | set helpVolumedown "

    Das VolumeDown muss eingegeben werden

    " 146 | } 147 | if {$messagevolume != ""} { 148 | if [string is integer $messagevolume] { 149 | set messagevolume [ expr round($messagevolume) ] 150 | if { $messagevolume > 100 || $messagevolume < 0 } { 151 | set helpMessagevolume "

    Das Message-Volume muss zwischen 0 und 100 sein

    " 152 | } 153 | } else { 154 | set helpMessagevolume "

    Das Message-Volume muss eine ganze Zahl sein

    " 155 | } 156 | } else { 157 | set helpMessagevolume "

    Das Message-Volume muss eingegeben werden

    " 158 | } 159 | if { "$helpMessagevolume$helpVolumedown$helpVolumeup$helpStdvolume$helpMessagespeicher$helpSonoszone$helpRadio$helpTimeout" != ""} { 160 | set message "$message [getMessage "Die Daten konnten wegen Eingabefehlern nicht gespeichert werden!" danger]" 161 | 162 | } else { 163 | set message "$message [getMessage "Daten gespeichert" success]" 164 | set Cfg::stdvolume $stdvolume 165 | set Cfg::volumeup $volumeup 166 | set Cfg::volumedown $volumedown 167 | set Cfg::messagevolume $messagevolume 168 | set Cfg::messagespeicher $messagespeicher 169 | set Cfg::timeout $timeout 170 | if { $::tcl_version != "8.2" } { 171 | set Cfg::sonoszone [regexp -all -inline {\S+} $sonoszone] 172 | } else { 173 | regsub -all {\s+} $sonoszone " " Cfg::sonoszone ;# whitespace zu Space 174 | } 175 | regsub -all "\r" $radio {} radio 176 | set radio [split $radio "\n"] 177 | set Cfg::radio {} 178 | foreach line $radio { 179 | set line [string trim $line] 180 | set sid [lindex $line 0] 181 | set station [string trim [join [lreplace $line 0 0 {}]]] 182 | lappend Cfg::radio $sid $station 183 | } 184 | Cfg::Save 185 | set button "reload" 186 | } 187 | } 188 | } else { 189 | set button "reload" 190 | } 191 | if {$button == "reload"} { 192 | Cfg::Load 193 | set radio "" 194 | regsub -all {(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s+} $Cfg::sonoszone "\\1\n" sonoszone ;#" %> 195 | foreach {sid station} $Cfg::radio { 196 | set radio "$radio\n$sid $station" 197 | } 198 | set timeout $Cfg::timeout 199 | set stdvolume $Cfg::stdvolume 200 | set volumeup $Cfg::volumeup 201 | set volumedown $Cfg::volumedown 202 | set messagevolume $Cfg::messagevolume 203 | set messagespeicher $Cfg::messagespeicher 204 | } 205 | set content [loadFile settings.html] 206 | set zone "" 207 | parseQuery 208 | if [info exists args(zone)] { 209 | if {$args(zone) != "" && $args(zone) != "fehlt"} { set zone "?zone=$args(zone)"} 210 | } 211 | regsub -all {<%zone%>} $content $zone content ;#" %> 212 | regsub -all {<%message%>} $content $message content ;#" %> 213 | regsub -all {<%sonoszone%>} $content "$sonoszone" content ;#" %> 214 | regsub -all {<%stdvolume%>} $content $stdvolume content ;#" %> 215 | regsub -all {<%volumeup%>} $content $volumeup content ;#" %> 216 | regsub -all {<%volumedown%>} $content $volumedown content ;#" %> 217 | regsub -all {<%messagevolume%>} $content $messagevolume content ;#" %> 218 | regsub -all {<%messagespeicher%>} $content $messagespeicher content ;#" %> 219 | regsub -all {<%timeout%>} $content $timeout content ;#" %> 220 | regsub -all {<%radio%>} $content $radio content ;#" %> 221 | regsub -all {<%helpSonoszone%>} $content "$helpSonoszone" content ;#" %> 222 | regsub -all {<%helpStdvolume%>} $content $helpStdvolume content ;#" %> 223 | regsub -all {<%helpVolumeup%>} $content $helpVolumeup content ;#" %> 224 | regsub -all {<%helpVolumedown%>} $content $helpVolumedown content ;#" %> 225 | regsub -all {<%helpMessagevolume%>} $content $helpMessagevolume content ;#" %> 226 | regsub -all {<%helpMessagespeicher%>} $content $helpMessagespeicher content ;#" %> 227 | regsub -all {<%helpTimeout%>} $content $helpTimeout content ;#" %> 228 | regsub -all {<%helpRadio%>} $content $helpRadio content ;#" %> 229 | 230 | puts "Content-type:text/html\n" 231 | puts $content 232 | -------------------------------------------------------------------------------- /www/settings/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Settings 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 27 | 28 | 29 | 45 | 72 | 73 | 74 | 75 |
    76 | <%message%> 77 | 78 | Settings 79 | 80 |
    81 |
    82 | 83 |
    84 | 85 |
    86 | 87 | <%helpSonoszone%> 88 |
    89 |
    90 | 91 | 92 |
    93 | 94 |
    95 | 97 |
    98 |
    99 | 100 |
    101 | 102 |
    103 | 104 | <%helpStdvolume%> 105 |
    106 |
    107 | 108 | 109 |
    110 | 111 |
    112 | 113 | <%helpVolumeup%> 114 |
    115 |
    116 | 117 | 118 |
    119 | 120 |
    121 | 122 | <%helpVolumedown%> 123 |
    124 |
    125 | 126 | 127 |
    128 | 129 |
    130 | 131 | <%helpMessagevolume%> 132 |
    133 |
    134 | 135 | 136 |
    137 | 138 |
    139 | 140 | <%helpMessagespeicher%> 141 |
    142 |
    143 | 144 |
    145 | 146 |
    147 | 148 | <%helpTimeout%> 149 |
    150 |
    151 | 152 | 153 |
    154 | 155 |
    156 | 157 | <%helpRadio%> 158 |
    159 |
    160 | 161 | 162 |
    163 | 164 |
    165 | 166 |
    171 | 172 |
    173 |
    174 |
    175 | 176 | 177 | 178 | 179 | 321 | 322 | -------------------------------------------------------------------------------- /www/settings/udp.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | source [file join [file dirname [info script]] ../sonos2inc.tcl] ;# Include-File 3 | Cfg::Load 4 | global ZoneGroupTopology 5 | set udp [Udp] 6 | if { $udp != "No Sonosplayer found!" } { 7 | proc GetChannelMapSet {i j {k ""}} { 8 | global ZoneGroupTopology 9 | if {$k !=""} {set k ",Satellite$k"} 10 | set ChannelMapSet "" 11 | if [info exists ZoneGroupTopology($i,$j$k,ChannelMapSet)] { 12 | regexp "$ZoneGroupTopology($i,$j$k,UUID):(\[^;\]*)" $ZoneGroupTopology($i,$j$k,ChannelMapSet) dummy ChannelMapSet 13 | } 14 | if [info exists ZoneGroupTopology($i,$j$k,HTSatChanMapSet)] { 15 | regexp "$ZoneGroupTopology($i,$j$k,UUID):(\[^;\]*)" $ZoneGroupTopology($i,$j$k,HTSatChanMapSet) dummy ChannelMapSet 16 | } 17 | regsub "LF,RF" $ChannelMapSet "-LR" ChannelMapSet 18 | regsub "LF,LF" $ChannelMapSet "-L" ChannelMapSet 19 | regsub "RF,RF" $ChannelMapSet "-R" ChannelMapSet 20 | regsub "SW" $ChannelMapSet "-Subwoofer" ChannelMapSet 21 | return $ChannelMapSet 22 | } 23 | proc GetDescription {url} { 24 | set descr [getHttp $url] 25 | set l {} 26 | regexp {\s*(.*?)\s*} $descr dummy descr 27 | if [Xml::Parse $descr friendlyName] { 28 | lappend l friendlyName $Xml::body 29 | } else { 30 | lappend l friendlyName {} 31 | } 32 | if [Xml::Parse $descr displayName] { 33 | lappend l displayName $Xml::body 34 | } else { 35 | lappend l displayName {} 36 | } 37 | if [Xml::Parse $descr roomName] { 38 | lappend l roomName $Xml::body 39 | } else { 40 | lappend l roomName {} 41 | } 42 | if [Xml::Parse $descr modelName] { 43 | lappend l modelName $Xml::body 44 | } else { 45 | lappend l modelName {} 46 | } 47 | if [Xml::Parse $descr icon] { 48 | if [Xml::Parse $Xml::body url] { 49 | lappend l icon $Xml::body 50 | } else { 51 | lappend l icon {} 52 | } 53 | } else { 54 | lappend l icon {} 55 | } 56 | return $l 57 | } 58 | 59 | set tt "" 60 | for {set i 1} {$i <= $ZoneGroupTopology(ZoneGroupCount)} {incr i} { 61 | set t "" 62 | set coordinator $ZoneGroupTopology($i,Coordinator) 63 | for {set j 1} {$j <= $ZoneGroupTopology($i,ZoneGroupMemberCount)} {incr j} { 64 | set ChannelMapSet [GetChannelMapSet $i $j] 65 | array set Description [GetDescription $ZoneGroupTopology($i,$j,Location)] 66 | regexp {http://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):1400/xml/device_description.xml} $ZoneGroupTopology($i,$j,Location) dummy Location 67 | # 68 | if {$ZoneGroupTopology($i,$j,Invisible) == "1"} { 69 | set inv "" 70 | } else { 71 | set inv "" 72 | } 73 | if {$ZoneGroupTopology($i,$j,IsZoneBridge) == "1"} { 74 | set coordStr " (Koordinator)" 75 | set cl " class='warning'" 76 | set bri "" 77 | set BRIDGE "$ZoneGroupTopology($i,$j,ZoneName) 78 | 79 | 80 | $ZoneGroupTopology($i,$j,ZoneName)$ChannelMapSet$coordStr$Location 81 | $Description(modelName)$bri$inv\n" 82 | } elseif {$coordinator == $ZoneGroupTopology($i,$j,UUID)} { 83 | #set t "ZoneGroup: $ZoneGroupTopology($i,$j,ZoneName) ID: $ZoneGroupTopology($i,ID)\n$t" 84 | set coordStr " (Koordinator)" 85 | set cl " class='active'" 86 | set bri "" 87 | set r "$ZoneGroupTopology($i,$j,ZoneName) 88 | 89 | " 90 | set t "$r$ZoneGroupTopology($i,$j,ZoneName)$ChannelMapSet$coordStr$Location 91 | $Description(modelName)$bri$inv\n$t" 92 | } else { 93 | set coordStr "" 94 | set cl "" 95 | set bri "" 96 | set t "$t 97 | " 98 | set t "$t$ZoneGroupTopology($i,$j,ZoneName)$ChannelMapSet$coordStr$Location 99 | $Description(modelName)$bri$inv\n" 100 | } 101 | #parray Description 102 | #$ZoneGroupTopology($i,$j,UUID)$coordStr 103 | for { set k 1} {$k <= $ZoneGroupTopology($i,$j,SatelliteCount)} {incr k} { 104 | set ChannelMapSet [GetChannelMapSet $i $j $k] 105 | array set Description [GetDescription $ZoneGroupTopology($i,$j,Satellite${k},Location)] 106 | regexp {http://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):1400/xml/device_description.xml} $ZoneGroupTopology($i,$j,Satellite${k},Location) dummy Location 107 | #set t "$t\t\tSatellite: $ZoneGroupTopology($i,$j,Satellite${k},ZoneName)$ChannelMapSet\t$ZoneGroupTopology($i,$j,UUID)$coordStr Location: $Location\n" 108 | if {$ZoneGroupTopology($i,$j,Satellite${k},Invisible) == "1"} { 109 | set inv "" 110 | } else { 111 | set inv "" 112 | } 113 | set bri "" 114 | set t "$t 115 | " 116 | set t "$t$ZoneGroupTopology($i,$j,Satellite${k},ZoneName)$ChannelMapSet (Satellite)$Location 117 | $Description(modelName)$bri$inv\n" 118 | } 119 | } 120 | set tt "$tt$t" 121 | } 122 | if {![info exists env(HTTP_HOST)] } { 123 | parray ZoneGroupTopology 124 | exit 125 | } 126 | } 127 | 128 | # read POST 129 | set message "" 130 | set info [encoding convertfrom utf-8 [url-decode [ gets stdin ]]] 131 | regexp "(js_aktiv=.*)" $info dummy info 132 | regsub -all "\r" $info {} info ;#" %> 133 | regsub -all {\&} $info "\r" info ;#" %> 134 | if { $info != "" } { 135 | foreach {line} [split $info "\r"] { 136 | switch -glob -- $line { 137 | udpbutton* { 138 | set Cfg::sonoszone $udp 139 | Cfg::Save 140 | set message "$message [getMessage "Zoneplayers in Config gespeichert" info]" 141 | } 142 | js_aktiv* { 143 | regexp "js_aktiv=(.*)" "$line" dummy js_aktiv 144 | if { $js_aktiv == "0"} { 145 | set message [getMessage "Sie haben Javascript deaktiviert, deshalb funktionieren manche Formulare nur eingeschränkt! Bitte aktivieren Sie wenn möglich Javascript" warning] 146 | } 147 | } 148 | } 149 | } 150 | } 151 | set content [loadFile udp.html] 152 | set zone "" 153 | parseQuery 154 | if [info exists args(zone)] { 155 | if {$args(zone) != "" && $args(zone) != "fehlt"} { set zone "?zone=$args(zone)"} 156 | } 157 | regsub -all {<%zone%>} $content $zone content ;#" %> 158 | regsub -all {<%message%>} $content $message content ;#" %> 159 | if { $udp != "No Sonosplayer found!" } { 160 | regsub -all {<%table%>} $content "$BRIDGE$tt" content ;#" %> 161 | } else { 162 | regsub -all {} $content [subst {
    163 |

    $udp

    164 |
    165 | Die UDP-Anfrage hat in Ihrem Netzwerk keine Sonosplayer gefunden. 166 |
    167 |
    168 | }] content ;#" %> 169 | } 170 | 171 | puts "Content-type:text/html\n" 172 | puts $content 173 | -------------------------------------------------------------------------------- /www/settings/udp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Topology 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 47 | 48 | 49 | 50 |
    51 | <%message%> 52 | 53 | Topology 54 |
    55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | <%table%> 68 | 69 |
    Zonegroup  ZonegroupmemberIPModel nameBridge?Visible?
    70 |
    71 |
    72 | 73 | 74 |
    75 |
    76 | 78 |
    79 |
    80 | 81 |
    82 |
    83 |

    Manchmal reagieren die Sonosplayer recht langsam, dann können falsche Ergebnisse resultieren!

    84 | 85 | 86 | 87 | 90 | 91 | -------------------------------------------------------------------------------- /www/sonos2.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | source [file join [file dirname [info script]] sonos2inc.tcl] ;# Include-File 3 | ##################################################################################### 4 | # 5 | # tlc version in Anlehnung an PHP-Version, Device-Spy und Wireshark 6 | # Datum diesr Version: 28.05.2015 7 | # Veröffentlicht im Forum: http://homematic-forum.de 8 | # Username: fiveyears 9 | # 10 | # Scriptaufruf: /usr/local/etc/config/addons/www/sonos2/sonos2.cgi zone action parameter 11 | # z. B.: /usr/local/etc/config/addons/www/sonos2/sonos2.cgi tv volume 30 12 | # 13 | # Grundlegender URL Aufbau: 14 | # ------------------------- 15 | # sonos2.cgi?zonen=SONOSPLAYER&action=BEFEHL&BEFEHL=Option 16 | # Beispiele: 17 | # sonos2.cgi?zonen=buero&action=play -> Absielen Starten 18 | # sonos2.cgi?zonen=buero&action=pause -> Abspielen Pausieren 19 | # sonos2.cgi?zonen=buero&action=next -> Nächster Titel 20 | # sonos2.cgi?zonen=buero&action=previous -> Verheriger Titel 21 | # sonos2.cgi?zonen=buero&action=mute&mute=false -> Sonos Laut schalten - false 22 | # sonos2.cgi?zonen=buero&action=mute&mute=true -> Sonos Leise schalten - true 23 | # sonos2.cgi?zonen=buero&message=1&volumen=20 -> Nachricht 1 anspielen mit Lautstärke 20 24 | # sonos2.cgi?zonen=buero&action=titelinfo -> einfache HTML Playlist Anzeige 25 | # sonos2.cgi?zonen=buero&action=addmember&member=kueche -> Zone Kueche zur Zone Buero hinzufügen 26 | # sonos2.cgi?zonen=buero&action=removemember&member=kueche -> Zone Kueche aus der Zone Buero entfernen 27 | # sonos2.cgi?zonen=schlafzimmer&action=sonosplaylist&playlist=Einschlafmusik&volume=1 28 | # sonos2.cgi?player=schlafzimmer&action=stop 29 | # sonos2.cgi?player=tv-zimmer&action=addmember&member=wohnzimmer 30 | # 31 | # 32 | # zonen= (wohnzimmer,tv,bad,kueche,schlafzimmer) -> kleinschreibung, keine Leer oder Sonderzeichen 33 | # player= (wohnzimmer,tv,bad,kueche,schlafzimmer) -> statt zonen geht auch player und zone 34 | # 35 | # action= 36 | # play -> Abspielen 37 | # pause -> Pause 38 | # stop -> Stop 39 | # toggle -> Play / Pause umschalten 40 | # next -> Next track in playlist 41 | # previous -> Previous track in playlist 42 | # rewind -> zum Anfang des tracks 43 | # settrack -> set track in playlist 44 | # seek -> seek relative in track 45 | # mute (true,false) -> Stumm schalten 46 | # shuffle (true,false) -> shuffle an/aus 47 | # repeat (true,false) -> repeat an/aus 48 | # crossfade (true,false) -> Titelübergang aus, aus 49 | # volume (0-100) -> Lautstärke setzen 50 | # ramp (0-100) -> auto ramp to Volume 51 | # sleep (0-100) -> sleep ramp to Volume 52 | # alarm (0-100) -> alarm ramp to Volume 53 | # volumeup -> Lautstärke um 3% erhöhen 54 | # volumedown -> Lautstärke um 5% verringern 55 | # addMember -> fügt Player zu Gruppe hinzu 56 | # member=Playername 57 | # removeMember -> entfernt Player aus Gruppe 58 | # member=Playername 59 | # partymodus -> alle Player in eine Gruppe 60 | # 61 | # playMessage -> entfernt Player aus Gruppe 62 | # message=file.ext -> z. B. action=message&message=Hallo.m4a 63 | # message=file.ext -> z. B. action=message&message=1.mp3 64 | # 65 | # info (Info-Name) -> zeigt Infos an 66 | # AudioInputAttributes 67 | # ZoneGroupAttributes 68 | # ZoneAttributes 69 | # ZoneInfo 70 | # Alarm -> Liste der Alarme, nicht formatiert 71 | # RadioIdUri -> ID: gut zum Anlegen neuer Sender und Radio-URI 72 | # MediaPositionInfo -> z. B. Sonos-URI, Sendername 73 | # CurrentPlaylist 74 | # mute 75 | # crossfademode 76 | # repeat 77 | # shuffle 78 | # volume 79 | # LEDState 80 | # AskRadio -> Info, was läuft 81 | # 82 | ######## Anzupassender Bereich ####################################################### 83 | 84 | # Konfiguration lesen 85 | 86 | set message "" 87 | Cfg::Load 88 | ######## Script Code ####################################################### 89 | parseQuery 90 | if {[info exists args(zone)]} { 91 | if { ! [sonosCreate $args(zone)] } { 92 | set sonosArray(Zone) "Sonoszone '$args(zone)' nicht gefunden!\n" 93 | } else { 94 | if {[info exists args(action)]} { 95 | set action $args(action) 96 | switch $action { 97 | message { 98 | if {[info exists args(message)]} { 99 | set vol $Cfg::messagevolume 100 | if {[info exists args(volume)]} { 101 | if {[ string is double $args(volume) ]} { 102 | set vol [ expr round($args(volume)) ] 103 | if { $vol > 100 } { set vol 100 } elseif { $vol < 0 } { set vol 0 } 104 | } 105 | } 106 | playMessage $args(message) $vol 107 | } 108 | } 109 | partymodus { 110 | setPartymodus 111 | 112 | } 113 | addmember { 114 | if {[info exists args(member)]} { 115 | addMember $args(member) 116 | } 117 | } 118 | removemember { 119 | if {[info exists args(member)]} { 120 | removeMember $args(member) 121 | } 122 | } 123 | radio { 124 | if {[info exists args(radio)]} { 125 | SetRadio $args(radio) 126 | GetPositionInfo 127 | Play 128 | } 129 | } 130 | mute { 131 | set mute [GetMute] 132 | if {[info exists args(mute)]} { 133 | set mute $args(mute) 134 | } else { 135 | if { $mute == "1" } { 136 | set mute 0 137 | } { 138 | set mute 1 139 | } } 140 | SetMute $mute 141 | } 142 | repeat { 143 | GetTransportSettings 144 | set shuffle [sonosGet shuffle] 145 | if {[info exists args(repeat)]} { 146 | set repeat $args(repeat) 147 | } else { 148 | set repeat 0 149 | } 150 | SetPlayMode $repeat $shuffle 151 | } 152 | shuffle { 153 | GetTransportSettings 154 | set repeat [sonosGet repeat] 155 | if {[info exists args(shuffle)]} { 156 | set shuffle $args(shuffle) 157 | } else { 158 | set shuffle 0 159 | } 160 | SetPlayMode $repeat $shuffle 161 | } 162 | crossfade { 163 | if {[info exists args(crossfade)]} { 164 | set crossfade $args(crossfade) 165 | } else { 166 | set crossfade "" 167 | } 168 | SetCrossfadeMode $crossfade 169 | } 170 | volume { 171 | if {[info exists args(volume)]} { 172 | set volume $args(volume) 173 | } else { 174 | set volume $Cfg::stdvolume 175 | } 176 | SetVolume $volume 177 | } 178 | ramp { 179 | if {[info exists args(ramp)]} { 180 | set volume $args(ramp) 181 | } else { 182 | set volume $Cfg::stdvolume 183 | } 184 | RampToVolume $volume [sonosGet Rampto] 185 | } 186 | sleep { 187 | if {[info exists args(sleep)]} { 188 | set volume $args(sleep) 189 | } else { 190 | set volume $Cfg::stdvolume 191 | } 192 | RampToVolume $volume sleep 193 | } 194 | alarm { 195 | if {[info exists args(alarm)]} { 196 | set volume $args(alarm) 197 | } else { 198 | set volume $Cfg::stdvolume 199 | } 200 | RampToVolume $volume alarm 201 | } 202 | volumeup { 203 | VolumeUp 204 | } 205 | volumedown { 206 | VolumeDown 207 | } 208 | play { 209 | Play 210 | } 211 | pause { 212 | Pause 213 | } 214 | stop { 215 | Stop 216 | } 217 | seek { 218 | if {[info exists args(relpos)]} { 219 | Seek $args(relpos) "NONE" 220 | } 221 | } 222 | next { 223 | Next 224 | } 225 | previous { 226 | Previous 227 | } 228 | settrack { 229 | if {[info exists args(tracknr)]} { 230 | SetTrack $args(tracknr) 231 | } 232 | } 233 | rewind { 234 | Rewind 235 | } 236 | toggle { 237 | Toggle 238 | } 239 | volumedown { 240 | VolumeDown 241 | } 242 | volumedown { 243 | VolumeDown 244 | } 245 | udp { 246 | set Cfg::sonoszone [Udp] 247 | Cfg::Save 248 | } 249 | } 250 | } 251 | } 252 | if [regexp ".*nicht gefunden.*" $sonosArray(Zone)] { 253 | set message [getMessage $sonosArray(Zone) danger] 254 | set sonosArray(Zone) "fehlt" 255 | } else { 256 | GetZoneAttributes 257 | if [info exists info(CurrentZoneName)] { 258 | set sonosArray(CurrentZoneName) $info(CurrentZoneName) 259 | init 260 | set player "${h1}Player: $sonosArray(CurrentZoneName)$unh1$hr" 261 | } else { 262 | set message [getMessage "Sonoszone '$sonosArray(Zone)' mit IP '$sonosArray(IP)' nicht erreichbar!" danger] 263 | set sonosArray(Zone) "fehlt" 264 | } 265 | } 266 | } { 267 | set message [getMessage "Keine Sonoszone angegeben!\n" info] 268 | set sonosArray(Zone) "fehlt" 269 | } 270 | if {$sonosArray(Zone) == "fehlt"} { 271 | init $message 272 | pBr "${h2}Sonosscript für die CCU2$unh2" 273 | pBr "${pre}Scriptaufruf: ${bold}/usr/local/etc/config/addons/www/sonos2/sonos2.cgi zone action parameter$unbold" 274 | pBr " Beispiel1: /usr/local/etc/config/addons/www/sonos2/sonos2.cgi tv play" 275 | pBr " Beispiel2: /usr/local/etc/config/addons/www/sonos2/sonos2.cgi tv volume 30" 276 | pBr "Browseraufruf: http://homematic-ccu2/addons/sonos2/sonos2.cgi?zone=${bold}ZONE$unbold&action=${bold}ACTION$unbold\[&ACTION=${bold}PARAMETER$unbold\]" 277 | pBr " Beispiel1: http://homematic-ccu2/addons/sonos2/sonos2.cgi?zone=tv&action=play" 278 | pBr " Beispiel2: http://homematic-ccu2/addons/sonos2/sonos2.cgi?zone=tv&action=volume&volume=30" 279 | pBr "Die Sonoszone (der Player) muss immer angegeben werden!$unpre" 280 | theEnd 281 | exit 282 | } 283 | 284 | GetSonosUUID 285 | 286 | if { [info exists info] } {unset info} 287 | if [info exists sonosArray(CurrentZoneName)] { 288 | unset sonosArray(Zone) 289 | } 290 | if {[info exists args(action)]} { 291 | set action $args(action) 292 | switch $action { 293 | message - 294 | partymodus - 295 | addmember - 296 | removemember - 297 | radio - 298 | mute - 299 | repeat - 300 | shuffle - 301 | crossfade - 302 | volume - 303 | ramp - 304 | sleep - 305 | alarm - 306 | volumeup - 307 | volumedown - 308 | play - 309 | pause - 310 | stop - 311 | seek - 312 | next - 313 | previous - 314 | settrack - 315 | rewind - 316 | stop - 317 | stop - 318 | toggle - 319 | volumedown - 320 | volumedown - 321 | udp - 322 | info { 323 | if {[info exists args(info)]} { 324 | switch -glob -- $args(info) { 325 | audioinputattribute* { 326 | pBr $player 327 | GetAudioInputAttributes 328 | listArray info "Audio-Inputattributes" 329 | listArray sonosArray "Player-Info" 330 | } 331 | zoneattribute* { 332 | GetZoneAttributes 333 | pBr $player 334 | listArray info "Zone-Attributes" 335 | listArray sonosArray "Player-Info" 336 | } 337 | zonegroupattribute* { 338 | pBr $player 339 | GetZoneGroupAttributes 340 | listArray info "Zonegroup-Attributes" 341 | listArray sonosArray "Player-Info" 342 | } 343 | zoneinf* { 344 | GetZoneInfo 345 | pBr $player 346 | listArray info "Zone-Info" 347 | listArray sonosArray "Player-Info" 348 | } 349 | transportinf* { 350 | GetTransportInfo 351 | pBr $player 352 | listArray info "Transport-Info" 353 | GetTransportSettings 354 | listArray sonosArray "Player-Info" 355 | } 356 | env { 357 | pBr $player 358 | listArray env 359 | listArray sonosArray "Player-Info" 360 | } 361 | alar* { 362 | pBr $player 363 | ListAlarms 364 | listArray info "Alarm-List" 365 | listArray sonosArray "Player-Info" 366 | } 367 | radioi* { 368 | pBr $player 369 | set id [IsRadio] 370 | if { $id == "0" } { 371 | List2Values "Attention:" "This is no radio station or id not found!" 372 | } { 373 | List2Values "Radio-ID:" $id 374 | List2Values "RadioURI:" [GetRadioUri] 375 | } 376 | listArray sonosArray "Player-Info" 377 | } 378 | positionmediainf* { 379 | pBr $player 380 | GetMediaInfo 381 | listArray info "Media-Info" 382 | unset info 383 | GetPositionInfo ; 384 | #GetCurrentPlaylist; 385 | listArray info "Position Info" 386 | listArray sonosArray "Player-Info" 387 | } 388 | currentplayli* { 389 | pBr $player 390 | GetCurrentPlaylist; 391 | listPlaylist playlist "Current Playlist   ($playlistcount Einträge)" 392 | listArray sonosArray "Player-Info" 393 | } 394 | sonosplayli* { 395 | pBr $player 396 | GetSonosPlaylists; 397 | listPlaylist playlist "Sonos Playlists   ($playlistcount Einträge)" 398 | listArray sonosArray "Player-Info" 399 | } 400 | importedplayli* { 401 | pBr $player 402 | GetImportedPlaylists; 403 | listPlaylist playlist "Imported Playlists   ($playlistcount Einträge)" 404 | listArray sonosArray "Player-Info" 405 | } 406 | 407 | mut* { 408 | pBr $player 409 | List2Values "Mutestate:" [GetMute] 410 | listArray sonosArray "Player-Info" 411 | } 412 | crossfade* { 413 | pBr $player 414 | List2Values "Crossfademode:"[GetCrossfadeMode] 415 | listArray sonosArray "Player-Info" 416 | } 417 | repea* { 418 | GetTransportSettings 419 | pBr $player 420 | List2Values "Repeat:" [sonosGet repeat] 421 | listArray sonosArray "Player-Info" "1" 422 | } 423 | shuf* { 424 | GetTransportSettings 425 | pBr $player 426 | List2Values "Shuffle:" [sonosGet shuffle] 427 | listArray sonosArray "Player-Info" 428 | } 429 | vol* { 430 | pBr $player 431 | List2Values "Volume:" [GetVolume] 432 | listArray sonosArray "Player-Info" 433 | } 434 | led* { 435 | pBr $player 436 | List2Values "LEDState:" [GetLEDState] 437 | listArray sonosArray "Player-Info" 438 | } 439 | askradio* { 440 | pBr $player 441 | AskRadiotime 442 | if { [info exists info(Image)]} { 443 | set image $info(Image) 444 | if { $br == "
    " } { 445 | puts "$image" 446 | unset info(Image) 447 | } 448 | } 449 | listArray info "Radio-Info" 450 | listArray sonosArray "Player-Info" 451 | } 452 | default { 453 | pBr 454 | listArray sonosArray "Player-Info" 455 | pBr "Info $bold'$args(info)'$unbold nicht gefunden!" 456 | } 457 | } 458 | } else { 459 | pBr $player 460 | listArray sonosArray "Player-Info" 461 | } 462 | } 463 | default { 464 | pBr $player 465 | listArray sonosArray "Player-Info" 466 | pBr "${bold}Action '$action' not recognized!$unbold" 467 | } 468 | } 469 | } { 470 | pBr $player 471 | listArray sonosArray "Player-Info" 472 | pBr "${bold}No action is given!$unbold" 473 | } 474 | theEnd -------------------------------------------------------------------------------- /www/status.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | source [file join [file dirname [info script]] sonos2inc.tcl] ;# Include-File 3 | set content [loadFile status.html] 4 | set zone "" 5 | parseQuery 6 | if [info exists args(zone)] { 7 | if {$args(zone) != "" && $args(zone) != "fehlt"} { set zone "?zone=$args(zone)"} 8 | } 9 | regsub -all {<%zone%>} $content $zone content ;#" %> 10 | puts "Content-type:text/html\n" 11 | puts $content 12 | -------------------------------------------------------------------------------- /www/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Status 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 47 | 48 | 49 | 50 |
    51 | 52 | Status 53 | 56 |
    57 |
    58 |
    59 |
    60 | 61 | 62 | 215 | 216 | -------------------------------------------------------------------------------- /www/update-check.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/tclsh 2 | 3 | set checkURL "https://raw.githubusercontent.com/homematic-community/hm-sonos/master/VERSION" 4 | set downloadURL "https://github.com/homematic-community/hm-sonos/releases" 5 | 6 | catch { 7 | set input $env(QUERY_STRING) 8 | set pairs [split $input &] 9 | foreach pair $pairs { 10 | if {$pair == "cmd=download"} { 11 | set cmd "download" 12 | break 13 | } 14 | } 15 | } 16 | 17 | if { [info exists cmd ] && $cmd == "download"} { 18 | puts -nonewline "Content-Type: text/html; charset=utf-8\r\n\r\n" 19 | puts -nonewline "" 20 | } else { 21 | puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n" 22 | catch { 23 | set newversion [ exec /usr/bin/wget -qO- --no-check-certificate $checkURL ] 24 | } 25 | if { [info exists newversion] } { 26 | puts $newversion 27 | } else { 28 | puts "n/a" 29 | } 30 | } 31 | --------------------------------------------------------------------------------