Ankündigung

Einklappen
Keine Ankündigung bisher.

- √ - Neues Plugin: Logitech Squeezebox - Anregungen?

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • macflei
    antwortet
    Zitat von Robert Beitrag anzeigen
    Wenn hier die Leute erzählen, Sprachnachrichten abspielen zu lassen - gibt dann eine fixe Playlist für Sprachnachrichten die dann geladen und ein bestimmter Index dann abgespielt wird? Was passiert dann mit der alten Playlist? Vor dem Wechsel "merken" und dann wieder laden? Oder geht das eleganter?
    Im konkreten Beispiel der Enertex.lib kann ich sagen, das die bei Alarm abzuspielende Datei als File oder Playlist vorliegen muß.
    Wenn ein Alarm auslöst und die SB gerade irgend eine Musik abspielt, wird diese in einer Temp gespeichert. Dann wird das "Alarmfile" abgespeielt (je nach Zeitvorgebe), und später das Temp wieder geladen.

    Code:
    // Squeezebox
    // @date    11.01.2012
    // @version    1 
    // @author    Enertex Bayern GmbH
    :begin SignalVar(PlayerID, Var, Zeit, Signal)
    :info $Wenn die Variable den Wert EIN annimmt, spielt die Squeezebox das Signal ab. Davor wird die aktuelle Playlist gespeichert und danach wieder geladen und die Variable auf AUS gesetzt. Kann man z.B. als Alarmsignal benutzen. Um dieses Makro verwenden zu können, müssen sie einmal das Makro "Squeezebox" eingebunden haben.$\\
        $PlayerID(MAC-Adresse oder Name) der Squeezebox als String, z.B. §00:04:20:12:85:fc§ oder §Squeezebox§ (Anstatt § das Dollarzeichen verwenden) $\\
        $Variable$\\
        $Gibt die Zeit in s an, wie lange das Signal abgespielt werden soll, bis die alte Playlist wieder geladen wird.(z.B. "30" für 30s)$\\
        $Relativer Pfad zum Signal, ausgehend vom Musik-Ordner.(z.B. "/Signale/Signal1.mp3")$
    :shortinfo $Die Squeezebox spielt über eine Variable ein Signal ab$
    if Var == EIN then connecttcp(ServerPort, ServerIP) endif
    if after(Var == EIN, TimeLag) and TCPConnected then {
        sendtcp(ServerPort, ServerIP, PlayerID + $ playlist save Temp$);
        sendtcp(ServerPort, ServerIP, PlayerID + $ playlist play ^Signal^$);
        }endif        
    if after(Var == EIN, Zeit^000u64 + 3000u64 + TimeLag) and TCPConnected then {
        sendtcp(ServerPort, ServerIP, PlayerID + $ playlist load Temp$);
        sendtcp(ServerPort, ServerIP, PlayerID + $ stop$);
        Var = AUS
        }endif
    irgendwo konnte man sogar festlegen mit welcher Lautstärke das "Alarfile" abgespeilt wird.

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Ja da hab ich ja die Befehle her! Ich bin allerdings aus der Beschreibung der Playlisten nicht schlau geworden, da es scheinbar teilweise um Vertauschoperationen geht (also Liste nur managen) und eben scheinbar um "Springen" in der Liste.

    Werde dann doch mal ein paar Playlists anlegen müssen um selber zu testen.

    Mit dem Interface - hm, schade, hatte nicht erwartet dass es da zig verschiedene gibt. So funktioniert das ganze sehr zuverlässig (auch eben mit korrekten Stati für Play, Pause, Stop) - nur die Playlisten-Geschichte erschließt sich mir halt momentan noch nicht.

    Wenn hier die Leute erzählen, Sprachnachrichten abspielen zu lassen - gibt dann eine fixe Playlist für Sprachnachrichten die dann geladen und ein bestimmter Index dann abgespielt wird? Was passiert dann mit der alten Playlist? Vor dem Wechsel "merken" und dann wieder laden? Oder geht das eleganter?

    Grüße
    Robert

    Einen Kommentar schreiben:


  • macflei
    antwortet
    vieleicht hilft unter Umständen auch die Docu unter http://IP-des-SBsever:9000/html/docs/index.html?player=

    Einen Kommentar schreiben:


  • Brick
    antwortet
    ich hatte mir mal einige Befehle notiert gehabt (für den Eibpc)..
    vielleicht kannst du davon ja welche brauchen:

    Code:
    00:xx:c6:1d:xx:dc show line1:Hello%20World line2:Second%20line duration:1 centered:1  (Text anzeigen)
    00:xx:c6:1d:xx:dc playlist index +1    (nächstes Lied)
    00:xx:c6:1d:xx:dc playlist index -1    (vorheriges Lied)
    00:xx:c6:1d:xx:dc mixer volume +10    (lauter)
    00:xx:c6:1d:xx:dc mixer volume -10    (leiser) 
    00:xx:c6:1d:xx:dc pause            (pause)
    00:xx:c6:1d:xx:dc playlist play file:///h:/mp3/mixmeister%2520remix.mp3   (Musiktitel abspielen)  leerzeichen durch %2520 erstzen !!!
    00:xx:c6:1d:xx:dc playlist play file:///h:/mp3/Funky%2520House%2520Electro%2520Mixtape.mp3
    00:xx:c6:1d:xx:dc playlist play file:///h:/OnProblemDetected_0407.mp3
    00:xx:c6:1d:xx:dc time ?               (Titelspielzeit abfragen)
    00:xx:c6:1d:xx:dc time XYZ        (Titelspielzeit an Pos. XYZ abspielen)
    00:xx:c6:1d:xx:dc time +60        (Titelzeit + 60 sec)
    00:xx:c6:1d:xx:dc power 0        (Ein/Aus)
    00:xx:c6:1d:xx:dc current_title ?    (Aktueller Titel)
    00:xx:c6:1d:xx:dc path ?        (Pfad zum Lied)
    00:xx:c6:1d:xx:dc Play X           (X = Sec. Fade in)
    00:xx:c6:1d:xx:dc playlist loadalbum <genre> <artist> <album>    
    00:xx:c6:1d:xx:dc playlist loadalbum * Labrassbanda *
    00:xx:c6:1d:xx:dc playlist loadalbum * Soulounge *
    00:xx:c6:1d:xx:dc playlist loadalbum * Loveparade%202008 *
    gibt aber natürlich noch deutlich mehr !

    Gruß Martin

    Einen Kommentar schreiben:


  • bluegaspode
    antwortet
    AW: Neues Plugin: Logitech Squeezebox - Anregungen?

    Wegen dem 'einheitlich antworten' : wie ich sehe nimmst du das telnet interface, welches das am schlechtesten gewartete von Logitech ist.
    Ein bisschen robuster ist die Kommunikation über JSon, so reden auch die Squeezeboxen mit dem Server (die zwar über einen cometd Kanal, das Nachrichtenformat ist aber gleich). JSon müsste sich doch auch mit Python deutlich leichter verarbeiten laden als selber telnet Antworten zu parsen ?
    Hast du unter forums.slimdevices.com schon mal nach jsonrpc gesucht. Da sollten eigentlich einige Artikel findbar sein.
    Was auch immer hilft: die Webseite des Squeezebox Servers aufmachen und die Entwicklungstools des Browsers aufmachen. Dann einfach die gewünschte Aktion auf der Webseite durchführen und das Netzwerkprotokoll des Browsers angucken.

    Wenn ich mich richtig erinnere, verwendet die Webseite auch den jsonrpc Endpunkt, so dass man sich dann das ganze ganz gut angucken kann.

    Einen Kommentar schreiben:


  • Robert
    hat ein Thema erstellt - √ - Neues Plugin: Logitech Squeezebox - Anregungen?.

    - √ - Neues Plugin: Logitech Squeezebox - Anregungen?

    Hallo smarthome.py-Nutzer,

    anbei findet ihr die "Beta" meines Plugins für die Logitech Squeezebox-Familie.

    "Beta", weil ich evtl. noch mal am Konzept feilen muss, da das LMS-Interface teilweise nicht ganz so einheitlich antwortet wie es für eine "glatte" Implementierung wünschenswert wäre. Daher sind schon einige Quirks eingebaut (Zustände Play, Stop, Pause, Volume bzw das Abfragen andersnamiger Felder beim Starten um Werte zu bekommen). Die in der untenstehenden Item-Definition "squeezebox.conf" aufgeführten Items funktionieren.

    Mithilfe wäre gewünscht, indem jemand mit Kenntnis der Playlist-Struktur sich mal mit Telnet auf dem LMS (ip:9090) einloggt, und mir näher bringt, wie ich jetzt eine "Standard-Playliste" lade und dann in dieser vor- und zurückspringen kann. Oder allgemein Playlisten. Mutige können auch versuchen die Item-Definitionen anzulegen.

    plugin/squeezebox/__init__.py
    Code:
    #!/usr/bin/env python
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #########################################################################
    # Copyright 2013 Robert Budde                        robert@projekt131.de
    #########################################################################
    #  Squeezebox-Plugin for SmartHome.py.  http://mknx.github.com/smarthome/
    #
    #  This plugin is free software: you can redistribute it and/or modify
    #  it under the terms of the GNU General Public License as published by
    #  the Free Software Foundation, either version 3 of the License, or
    #  (at your option) any later version.
    #
    #  This plugin is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this plugin. If not, see <http://www.gnu.org/licenses/>.
    #########################################################################
    
    import logging
    import struct
    import time
    import urllib2
    import lib.my_asynchat
    import re
    
    logger = logging.getLogger('Squeezebox')
    
    class Squeezebox(lib.my_asynchat.AsynChat):
    
        def __init__(self, smarthome, host='127.0.0.1', port=9090):
            lib.my_asynchat.AsynChat.__init__(self, smarthome, host, port)
            self._sh = smarthome
            self._val = {}
            self._obj = {}
            self._init_cmds = []
            smarthome.monitor_connection(self)
    
        def _check_mac(self, mac):
            return re.match("[0-9a-f]{2}([:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower())
        
        def _resolv_full_cmd(self, item, attr):
            if (item.conf[attr][0] == '*'):
                # check if PlayerID (MAC) is given - if not get from parent object
                parent_item = item.return_parent()
                if (parent_item != None) and ('squeezebox_playerid' in parent_item.conf) and self._check_mac(parent_item.conf['squeezebox_playerid']):
                    item.conf[attr] = parent_item.conf['squeezebox_playerid'] + item.conf[attr][1::]
                else:
                    logger.warning("squeezebox: could not resolve playerid for {0} from parent item {1}".format(item, parent_item))
                    return None
            return item.conf[attr] 
    
        def parse_item(self, item):
            
            if 'squeezebox_recv' in item.conf:         
                cmd = self._resolv_full_cmd(item,'squeezebox_recv')
                if (cmd == None):
                    return None 
                
                logger.debug("squeezebox: {0} receives updates by {1}".format(item, cmd))
                if not cmd in self._val:
                    self._val[cmd] = {'items': [item], 'logics': []}
                else:
                    if not item in self._val[cmd]['items']:
                        self._val[cmd]['items'].append(item)
    
                if ('squeezebox_init' in item.conf):
                    cmd = self._resolv_full_cmd(item,'squeezebox_init')
                    if (cmd == None):
                        return None 
                    
                    logger.debug("squeezebox: {0} is initialized by {1}".format(item, cmd))
                    if not cmd in self._val:
                        self._val[cmd] = {'items': [item], 'logics': []}
                    else:
                        if not item in self._val[cmd]['items']:
                            self._val[cmd]['items'].append(item)
                            
                if not cmd in self._init_cmds:
                    self._init_cmds.append(cmd)
                
            if 'squeezebox_send' in item.conf:         
                cmd = self._resolv_full_cmd(item,'squeezebox_send')
                if (cmd == None):
                    return None 
                
                logger.debug("squeezebox: {0} is send to {1}".format(item, cmd))
                return self.update_item
            else:        
                return None
    
        def parse_logic(self, logic):
            pass
    
        def update_item(self, item, caller=None, source=None, dest=None):
            # be careful: as the server echoes ALL comands not using this will result in a loop
            if caller != 'Squeezebox Media Server':
                cmd = self._resolv_full_cmd(item, 'squeezebox_send').split()
                if self._check_mac(cmd[0]):
                    cmd[0] = urllib2.quote(cmd[0]) 
                if isinstance(item, str):
                    value = urllib2.quote(item())
                elif (item._type == 'bool'):
                    # convert to get '0'/'1' instead of 'True'/'False'
                    value = int(item()) 
                else:
                    value = item()
                    
                # special handling
                if (len(cmd) >= 2) and (cmd[1] == 'play') and not item():
                    # if 'play' was set to false, send 'pause'
                    cmd[1] = 'pause' 
                self._send(' '.join(cmd_str for cmd_str in cmd).format(value))
    
        def _send(self, cmd):
            logger.debug("Sending request: {0}".format(cmd))
            self.push(cmd+'\r\n')
    
        def _parse_response(self, response):
            data = [urllib2.unquote(data_str) for data_str in response.split()]
            logger.debug("Got: {0}".format(data))
            
    #        if (data[0].lower() == 'player'):
    #            if (data[1].lower() == 'count'):
    #                num_players = int(data[2])
    #                logger.info("Found {0} player".format(num_players))
    #                for i in range(0,num_players):
    #                    self._send("player name {0} ?".format(i))
    #                    self._send("player id {0} ?".format(i))                    
    #            elif (data[1].lower() == 'name'):
    #                logger.info("Players {0} name is: {1}".format(data[2], data[3]))
    #            elif (data[1].lower() == 'id'):
    #                logger.info("Players {0} id is: {1}".format(data[2], data[3]))
            if (data[0].lower() == 'listen'):
                value = int(data[1]) 
                if (value == 1):
                    logger.info("Listen-mode enabled")
                else:
                    logger.info("Listen-mode disabled")
    
            if self._check_mac(data[0]):
                if (data[1] == 'play'):
                    self._update_items_with_data([data[0], 'play', 1])
                    self._update_items_with_data([data[0], 'stop', 0])
                    self._update_items_with_data([data[0], 'pause', 0])
                    return
                elif (data[1] == 'stop'):
                    self._update_items_with_data([data[0], 'play', 0])
                    self._update_items_with_data([data[0], 'stop', 1])
                    self._update_items_with_data([data[0], 'pause', 0])
                    return
                elif (data[1] == 'pause'):
                    self._send(data[0] + ' mode ?')
                    return
                elif (data[1] == 'mode'):
                    self._update_items_with_data([data[0], 'play', data[2] == 'play'])
                    self._update_items_with_data([data[0], 'stop', data[2] == 'stop'])
                    self._update_items_with_data([data[0], 'pause', data[2] == 'pause'])
                    return
                elif (data[1] == 'prefset'):
                    if (data[2] == 'server'):
                        if (data[3] == 'volume'):
                            # make sure value is always positive - also if muted!
                            data[4] = abs(int(data[4]))
                elif (data[1] == 'playlist'):
                    if (data[2] == 'newsong'):
                        # trigger reading of other song fields
                        for field in ['genre', 'artist', 'album', 'title', 'duration']:
                            self._send(data[0] + ' ' + field + ' ?')
                elif (data[1] in ['genre', 'artist', 'album', 'title', 'duration']) and (len(data) == 2):
                    # these fields are returned empty so no update takes plase - append '' to trigger update
                    data.append('')           
                    
            self._update_items_with_data(data)
                                    
        def _update_items_with_data(self, data):        
            cmd = ' '.join(data_str for data_str in data[:-1])
            if (cmd in self._val):
                for item in self._val[cmd]['items']:                  
                    item(data[-1], 'Squeezebox Media Server', "{}:{}".format(self.addr[0],self.addr[1]))
                     
        def found_terminator(self):
            response = self.buffer
            self.buffer = ''
            self._parse_response(response)
    
        def handle_connect(self):
            self.discard_buffers()
            # start discovering players and their respective IDs and names        
    #        self._send('players 0')
            self._send('listen 1')
            if self._init_cmds != []:
                if self.is_connected:
                    logger.debug('squeezebox: init read')
                    for cmd in self._init_cmds:
                        self._send(cmd+' ?')
            
        def run(self):
            self.alive = True
    
        def stop(self):
            self.alive = False
            self.handle_close()
    etc/plugin.conf
    Code:
    [squeezebox]
        class_name = Squeezebox
        class_path = plugins.squeezebox
    items/squeezebox.conf
    Code:
    [Squeezebox_2]
      squeezebox_playerid = 00:04:20:27:ca:1f
    
      [[Name]]
        type = str
        visu = yes
        squeezebox_recv = * player name ?   
        squeezebox_init = * player name    
      [[IP]]
        type = str
        visu = yes
        squeezebox_recv = * player ip ?   
        squeezebox_init = * player ip    
      [[Signal_Strength]]
        type = num
        visu = yes
        squeezebox_recv = * signalstrength    
    
      [[Power]]
        type = bool
        visu = yes
        squeezebox_send = * power {}
        squeezebox_recv = * prefset server power
        squeezebox_init = * power    
        
      [[Mute]]
        type = bool
        visu = yes
        squeezebox_send = * mixer muting {}
        squeezebox_recv = * prefset server mute
        squeezebox_init = * mixer muting
      [[Volume]]
        type = num
        visu = yes
        squeezebox_send = * mixer volume {}
        squeezebox_recv = * prefset server volume
        squeezebox_init = * mixer volume    
      [[Volume_Up]]
        type = bool
        enforce_updates = true
        visu = yes
        squeezebox_send = * button volup
      [[Volume_Down]]
        type = bool
        enforce_updates = true
        visu = yes
        squeezebox_send = * button voldown
        
      [[Play]]
        type = bool
        visu = yes
        squeezebox_send = * play
        squeezebox_recv = * play
        squeezebox_init = * mode
      [[Stop]]
        type = bool
        visu = yes
        squeezebox_send = * stop
        squeezebox_recv = * stop
        squeezebox_init = * mode
      [[Pause]]
        type = bool
        visu = yes
        squeezebox_send = * pause {}
        squeezebox_recv = * pause
        squeezebox_init = * mode
     
      [[Current_Title]]
        type = str
        visu = yes
        squeezebox_recv = * playlist newsong
        squeezebox_init = * current_title
    
      [[Genre]]
        type = str
        visu = yes
        squeezebox_recv = * genre
      [[Artist]]
        type = str
        visu = yes
        squeezebox_recv = * artist
      [[Album]]
        type = str
        visu = yes
        squeezebox_recv = * album
      [[Title]]
        type = str
        visu = yes
        squeezebox_recv = * title
      [[Duration]]
        type = num
        visu = yes
        squeezebox_recv = * duration

    Kurzerklärung:

    • squeezebox_playerid = MAC der Squeezebox
    • squeezebox_recv = String der das Item updated (von LMS gesendet)
      • * bedeutet player-id vom parent_item nehmen - sonst direkt angeben! So kann man einfacher bei mehreren Squeezeboxen die Items kopieren.
      • ? ist ein extra Zeichen (manchmal braucht es zwei Fragezeichen - eins wird automatisch angefügt!)

    • squeezebox_init = String der zu Anfang das Item initialisiert (manchmal ist dieser anders als squeezebox_recv)
      • wird squeezebox_init nicht angegeben, wird squeezebox_recv beim Start abgefragt!

    • squeezebox_send = String zum Senden zum LMS
      • {} ist für das Eintragen des Value - fehlt dies, wird kein Value gesendet ('play' etc.!)


    Viel Spaß!


    Grüße
    Robert
    Angehängte Dateien
Lädt...
X