Zitat von macflei
Beitrag anzeigen
Danke dir!
Songnamen sind schon gefixt - nimm bitte die angehängte Version. Da fehlte die Konvertierung von UTF-8.
Was die Playlist angeht komm ich grad nicht mit - wie du richtig sagst geht vor/zurück. Aber eine neue Playlists geht momentan nur per fixem Boolean-Item (siehe Playlist_Load_Internetradio), da ich keine Möglichkeit kenne einen String auszuwählen?
Achtung: Statt * wird nun für <playerid> die ID der jeweiligen Squeezebox gesetzt - wurde benötigt da manchmal die ID nicht nur an erster Stelle steht und mir * dann einfach zu gefährlich war.
Code:
#!/usr/bin/env python # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab ######################################################################### # Copyright 2013 Robert Budde [EMAIL="robert@projekt131.de"]robert@projekt131.de[/EMAIL] ######################################################################### # Squeezebox-Plugin for SmartHome.py. [url=http://mknx.github.com/smarthome/]SmartHome.py - Overview[/url] # # 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 <[url=http://www.gnu.org/licenses/]Licenses - GNU Project - Free Software Foundation (FSF)[/url]>. ######################################################################### 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}([URL="file://\\1"]\\1[/URL][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() == '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]) # 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', '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']: if isinstance(item(), str): 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() # 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()
Code:
# Hobbyraum/Experimental [Squeezebox_1] squeezebox_playerid = 00:04:20:xx:xx:xx [[Name]] type = str visu = yes squeezebox_send = <playerid> name {} squeezebox_recv = <playerid> name [[IP]] type = str visu = yes squeezebox_recv = player ip <playerid> [[Signal_Strength]] type = num visu = yes squeezebox_recv = <playerid> signalstrength [[Power]] type = bool visu = yes squeezebox_send = <playerid> power {} squeezebox_recv = <playerid> prefset server power squeezebox_init = <playerid> power [[Mute]] type = bool visu = yes squeezebox_send = <playerid> mixer muting {} squeezebox_recv = <playerid> prefset server mute squeezebox_init = <playerid> mixer muting [[Volume]] type = num visu = yes squeezebox_send = <playerid> mixer volume {} squeezebox_recv = <playerid> prefset server volume squeezebox_init = <playerid> mixer volume [[Volume_Up]] type = bool enforce_updates = true visu = yes squeezebox_send = <playerid> button volup [[Volume_Down]] type = bool enforce_updates = true visu = yes squeezebox_send = <playerid> button voldown [[Play]] type = bool visu = yes squeezebox_send = <playerid> play squeezebox_recv = <playerid> play squeezebox_init = <playerid> mode [[Stop]] type = bool visu = yes squeezebox_send = <playerid> stop squeezebox_recv = <playerid> stop squeezebox_init = <playerid> mode [[Pause]] type = bool visu = yes squeezebox_send = <playerid> pause {} squeezebox_recv = <playerid> pause squeezebox_init = <playerid> mode [[Current_Title]] type = str visu = yes squeezebox_recv = <playerid> playlist newsong squeezebox_init = <playerid> current_title [[Genre]] type = str visu = yes squeezebox_recv = <playerid> genre [[Artist]] type = str visu = yes squeezebox_recv = <playerid> artist [[Album]] type = str visu = yes squeezebox_recv = <playerid> album [[Title]] type = str visu = yes squeezebox_recv = <playerid> title [[Duration]] type = num visu = yes squeezebox_recv = <playerid> duration [[Playlist_Index]] type = num visu = yes squeezebox_send = <playerid> playlist index {} squeezebox_recv = <playerid> playlist index [[Playlist_Forward]] type = bool enforce_updates = true visu = yes squeezebox_send = <playerid> playlist index +1 [[Playlist_Backward]] type = bool enforce_updates = true visu = yes squeezebox_send = <playerid> playlist index -1 [[Playlist_Name]] type = str visu = yes squeezebox_send = <playerid> playlist name {} squeezebox_recv = <playerid> playlist name [[Playlist_Save]] type = str visu = yes squeezebox_send = <playerid> playlist save {} [[Playlist_Load]] type = str enforce_updates = true visu = yes squeezebox_send = <playerid> playlist play {} [[Playlist_Load_Internetradio]] type = bool enforce_updates = true visu = yes squeezebox_send = <playerid> playlist play [URL]file:///home/robert/playlists/Internetradio.m3u[/URL]
Kommentar