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