Ankündigung

Einklappen
Keine Ankündigung bisher.

- √ - Neues Plugin: Logitech Squeezebox - Anregungen?

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

  • Robert
    antwortet
    Ja, wenn du dem Item auch ein knx_send etc gibts - warum nicht?

    Du kannst auch mit dem Lautstärkeregler deine Raffstores steuern... ;-)

    Ausprobieren - wenn es nicht klappt -> Bug melden

    Einen Kommentar schreiben:


  • henfri
    antwortet
    Hallo Robert,

    ich würde gerne ein Squeezebox Radio nutzen um Aktionen auszuführen.
    Wenn ich z.B. das Radio auf Play stelle, dann soll per KNX der Verstärker eingeschaltet werden.

    Geht das mit dem Plugin? Bekommt das Plugin mit, wenn Musik abgespielt wird (klar, wenn ich per Plugin auf Play gehe geht das, aber was ist, wenn ich den Knopf am Gerät drücke?)

    Gruß,
    Hendrik

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Zitat von macflei Beitrag anzeigen
    [...] schön wäre, wenn man noch "Repeat Song", "Repeat Playlist", "Zufall Song", "Zuafall Album" in das Plugin einbauen könnte.
    Das kann man schon. Einfach mit squeezebox_send/squeezebox_recv spielen.

    Wiederholungsmodus per Nummer setzen/lesen
    Code:
    [Repeat]
       type = num
       force_updates = yes
       squeezebox_send = <playerid> playlist repeat {}
       squeezebox_recv = <playerid> playlist repeat
    Wiederholen des aktuellen Songs per Bool
    Code:
    [Repeat_Song]
       type = bool
       force_updates = yes
       squeezebox_send = <playerid> playlist repeat 1
    Wiederholen der aktuellen Playlist per Bool
    Code:
    [Repeat_Playlist]
       type = bool
       force_updates = yes
       squeezebox_send = <playerid> playlist repeat 2
    Wiederholen ausschalten per Bool
    Code:
    [Repeat_None]
       type = bool
       force_updates = yes
       squeezebox_send = <playerid> playlist repeat 0
    analog für "shuffle"

    Wenn das nicht wie gewünscht funktioniert -> Bug melden

    Motto: so wenig Quirks wie möglich...

    Grüße
    Robert

    Einen Kommentar schreiben:


  • Robert
    antwortet
    https://knx-user-forum.de/smartvisu/...te-widget.html !!!!!

    Da ist alles drin, inkl. widget_squeezebox und einer Demoeinbindung inkl. Signalstärke etc. Bitte lesen und mithelfen - am besten wäre wenn wir gemeinsam ein richtig "dicker" Squeezebox"-Widget bauen, was auch Playlisten kann!

    Einen Kommentar schreiben:


  • macflei
    antwortet
    Da das Multimedia-widget nicht gerade optimal für den Sqeezebox-Player ist (z.B. Song zurück), bastel ich gerade einen angepassten.
    Dabei ist mir aufgefallen das es doch schön wäre, wenn man noch "Repeat Song", "Repeat Playlist", "Zufall Song", "Zuafall Album" in das Plugin einbauen könnte.

    Einen Kommentar schreiben:


  • macflei
    antwortet
    Zitat von Robert Beitrag anzeigen
    Fix für das Problem mit den Umlauten und fix dass die Spielzeit nicht auf 0 gesetzt wurde:
    Spitze ...... läuft. Danke

    Zitat von JuMi2006 Beitrag anzeigen
    Anleitung für den LMS auf dem Pi:
    All Things Pi
    Ja. Hatte ich auch 2 mal versucht. Aber immer wieder Probleme.

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Anleitung für den LMS auf dem Pi:

    All Things Pi

    Einen Kommentar schreiben:


  • Robert
    antwortet
    (ab morgen gibt es das Plugin mit Doku im GIT-Repo)

    Fix für das Problem mit den Umlauten und fix dass die Spielzeit nicht auf 0 gesetzt wurde:

    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):
            # check if PlayerID wildcard is used
            if '<playerid>' in item.conf[attr]:
                # try to 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] = item.conf[attr].replace('<playerid>', parent_item.conf['squeezebox_playerid'])
                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 != 'LMS':
                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().encode('utf-8'))
                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):
                    if (cmd[1] == 'play') and not item():
                        # if 'play' was set to false, send 'stop' to allow single-item-operation
                        cmd[1] = 'stop'
                        value = '1'
                    elif (cmd[1] == 'pause') and not item():
                        # squeezebox un-mutes when un-pausing but does not update the state itself - unmute manually to reflect actual state correctly
                        self._send(cmd[0] + ' mixer muting 0')
                        
                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() == '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])
                    # play also overrules mute
                    self._update_items_with_data([data[0], 'prefset server mute', 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 ?')
                    self._send(data[0] + ' mixer muting ?')
                    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'])
                    # play also overrules mute
                    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']) and (len(data) == 2):
                    # these fields are returned empty so no update takes plase - append '' to trigger update
                    data.append('')           
                elif (data[1] in ['duration']) and (len(data) == 2):
                    # these fields are returned empty so no update takes plase - append '0' to trigger update
                    data.append('0')           
                    
            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']:
                    if isinstance(item(), str) or isinstance(item(), unicode):
                        data[-1] = data[-1].decode('utf-8')            
                    item(data[-1], 'LMS', "{}:{}".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()
            # enable listen-mode to get notified of changes        
            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()

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Hi!

    Ja, sorry - ihr habt euch ja schon geholfen. *g*

    Klar, in der plugin.conf gehören noch optional
    Code:
        host = xxx.xxx.xxx.xxx
        port = xxxx
    Ich bin dabei den Bug den macflei gemeldet hat zu fixen...

    Einen Kommentar schreiben:


  • macflei
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Wie soll ich das der Gattin vermitteln
    Der fällt im Rack doch gar nicht auf und ist über Flaschenpfand zu finanzieren

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Danke dann muss ich da mal schnüffeln, nen dritten Pi stell ich mir jetzt nicht noch hin . Wie soll ich das der Gattin vermitteln

    Einen Kommentar schreiben:


  • macflei
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Localhost kann ja default sein und wird, wenn gesetzt überschrieben. Man sollte als user nicht in der __init__.py rumwühlen müssen. Das bringt am Ende nur unnötigen Supportaufwand.
    Achso ...ja, ok. Da ist was dran

    Zitat von JuMi2006 Beitrag anzeigen
    Edit:Hat jemand den lms auf dem Pi am rennen? Hier mag er irgendwie nicht.
    Ja aber auf einem 2ten Pi. Also nicht auf dem wo sV+Sh.py läuft.
    Es gibt dafür auch fertige Images. Auch genial einfach.Google mal nach squeezeplug

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Nein, also der Server gehört doch mit in die Plugin-Konfiguration:

    [squeezebox]
    class_name = Squeezebox
    class_path = plugins.squeezebox
    server_ip = 192.168.2.222
    server_port = 9090
    Localhost kann ja default sein und wird, wenn gesetzt überschrieben. Man sollte als user nicht in der __init__.py rumwühlen müssen. Das bringt am Ende nur unnötigen Supportaufwand.

    Ansonsten ... was soll ich sagen ... läuft !!!

    Edit:Hat jemand den lms auf dem Pi am rennen? Hier mag er irgendwie nicht.

    Einen Kommentar schreiben:


  • macflei
    antwortet
    hat er ja geschrieben ..... zu Beginn

    [squeezebox]
    class_name = Squeezebox
    class_path = plugins.squeezebox

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    o.k. ... das muss dann aber noch in die plugin.conf
    Also Robert ... tztztz

    Dank und Gruß!

    Einen Kommentar schreiben:

Lädt...
X