# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2006-2010 Frank Scholz <dev@coherence-project.org>
import traceback
from twisted.internet import reactor
from twisted.web import xmlrpc, client
import coherence.extern.louie as louie
from coherence import log
from coherence.upnp.core import service
from coherence.upnp.core.event import EventServer
from coherence.upnp.devices.internet_gateway_device_client import \
InternetGatewayDeviceClient
from coherence.upnp.devices.media_renderer_client import MediaRendererClient
from coherence.upnp.devices.media_server_client import MediaServerClient
[docs]class DeviceQuery(object):
def __init__(self, type, pattern, callback, timeout=0, oneshot=True):
self.type = type
self.pattern = pattern
self.callback = callback
self.fired = False
self.timeout = timeout
self.oneshot = oneshot
if self.type == 'uuid' and self.pattern.startswith('uuid:'):
self.pattern = self.pattern[5:]
[docs] def fire(self, device):
if callable(self.callback):
self.callback(device)
elif isinstance(self.callback, str):
louie.send(self.callback, None, device=device)
self.fired = True
[docs] def check(self, device):
if self.fired and self.oneshot:
return
if (self.type == 'host' and
device.host == self.pattern):
self.fire(device)
elif (self.type == 'friendly_name' and
device.friendly_name == self.pattern):
self.fire(device)
elif (self.type == 'uuid' and
device.get_uuid() == self.pattern):
self.fire(device)
[docs]class ControlPoint(log.LogAble):
logCategory = 'controlpoint'
def __init__(self, coherence, auto_client=None):
log.LogAble.__init__(self)
if not auto_client:
auto_client = ['MediaServer', 'MediaRenderer']
self.coherence = coherence
self.auto_client = auto_client
self.queries = []
self.info("Coherence UPnP ControlPoint starting...")
self.event_server = EventServer(self)
self.coherence.add_web_resource('RPC2', XMLRPC(self))
for device in self.get_devices():
self.info('ControlPoint [check device]: {}'.format(device))
self.check_device(device)
louie.connect(self.check_device,
'Coherence.UPnP.Device.detection_completed',
louie.Any)
louie.connect(self.remove_client,
'Coherence.UPnP.Device.remove_client',
louie.Any)
louie.connect(self.completed,
'Coherence.UPnP.DeviceClient.detection_completed',
louie.Any)
[docs] def shutdown(self):
louie.disconnect(self.check_device,
'Coherence.UPnP.Device.detection_completed',
louie.Any)
louie.disconnect(self.remove_client,
'Coherence.UPnP.Device.remove_client',
louie.Any)
louie.disconnect(self.completed,
'Coherence.UPnP.DeviceClient.detection_completed',
louie.Any)
[docs] def auto_client_append(self, device_type):
if device_type in self.auto_client:
return
self.auto_client.append(device_type)
for device in self.get_devices():
self.check_device(device)
[docs] def browse(self, device):
self.info('ControlPoint.browse: {}'.format(device))
device = self.coherence.get_device_with_usn(device.get_usn())
if not device:
return
self.check_device(device)
[docs] def process_queries(self, device):
for query in self.queries:
query.check(device)
[docs] def add_query(self, query):
for device in self.get_devices():
query.check(device)
if not query.fired and query.timeout == 0:
query.callback(None)
else:
self.queries.append(query)
[docs] def connect(self, receiver, signal=louie.signal.All,
sender=louie.sender.Any, weak=True):
""" wrapper method around louie.connect
"""
louie.connect(receiver, signal=signal, sender=sender, weak=weak)
[docs] def disconnect(self, receiver, signal=louie.signal.All,
sender=louie.sender.Any, weak=True):
""" wrapper method around louie.disconnect
"""
louie.disconnect(receiver, signal=signal, sender=sender, weak=weak)
[docs] def get_devices(self):
return self.coherence.get_devices()
[docs] def get_device_with_id(self, id):
return self.coherence.get_device_with_id(id)
[docs] def get_device_by_host(self, host):
return self.coherence.get_device_by_host(host)
[docs] def check_device(self, device):
if device.client is None:
self.info("found device %s of type %s - %r",
device.get_friendly_name(),
device.get_device_type(), device.client)
short_type = device.get_friendly_device_type()
if short_type in self.auto_client and short_type is not None:
self.info("identified %s %r", short_type,
device.get_friendly_name())
if short_type == 'MediaServer':
client = MediaServerClient(device)
if short_type == 'MediaRenderer':
client = MediaRendererClient(device)
if short_type == 'InternetGatewayDevice':
client = InternetGatewayDeviceClient(device)
client.coherence = self.coherence
device.set_client(client)
self.process_queries(device)
[docs] def completed(self, client, udn):
self.info('sending signal Coherence.UPnP.ControlPoint.%s.detected %r',
client.device_type, udn)
louie.send(
'Coherence.UPnP.ControlPoint.%s.detected' % client.device_type,
None,
client=client, udn=udn)
[docs] def remove_client(self, udn, client):
louie.send(
'Coherence.UPnP.ControlPoint.%s.removed' % client.device_type,
None, udn=udn)
self.info("removed %s %s", client.device_type,
client.device.get_friendly_name())
client.remove()
[docs] def propagate(self, event):
self.info('propagate: %r', event)
if event.get_sid() in service.subscribers:
try:
service.subscribers[event.get_sid()].process_event(event)
except Exception as msg:
self.debug(msg)
self.debug(traceback.format_exc())
pass
[docs] def put_resource(self, url, path):
def got_result(result):
print(result)
def got_error(result):
print("error", result)
try:
f = open(path)
data = f.read()
f.close()
headers = {
b"Content-Type": b"application/octet-stream",
b"Content-Length": bytes(str(len(data)), encoding='utf-8')
}
df = client.getPage(
url, method=b"POST",
headers=headers, postdata=data)
df.addCallback(got_result)
df.addErrback(got_error)
return df
except IOError:
pass
[docs]class XMLRPC(xmlrpc.XMLRPC):
def __init__(self, control_point):
xmlrpc.XMLRPC.__init__(self)
self.control_point = control_point
self.allowNone = True
[docs] def xmlrpc_list_devices(self):
print("list_devices")
r = []
for device in self.control_point.get_devices():
# print(device.get_friendly_name(), device.get_service_type(),
# device.get_location(), device.get_id())
d = {'friendly_name': device.get_friendly_name(),
'device_type': device.get_device_type(),
'location': str(device.get_location()),
'id': str(device.get_id())}
r.append(d)
return r
[docs] def xmlrpc_mute_device(self, device_id):
print("mute")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.rendering_control.set_mute(desired_mute=1)
return "Ok"
return "Error"
[docs] def xmlrpc_unmute_device(self, device_id):
print("unmute", device_id)
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.rendering_control.set_mute(desired_mute=0)
return "Ok"
return "Error"
[docs] def xmlrpc_set_volume(self, device_id, volume):
print("set volume")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.rendering_control.set_volume(desired_volume=volume)
return "Ok"
return "Error"
[docs] def xmlrpc_play(self, device_id):
print("play")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.av_transport.play()
return "Ok"
return "Error"
[docs] def xmlrpc_pause(self, device_id):
print("pause")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.av_transport.pause()
return "Ok"
return "Error"
[docs] def xmlrpc_stop(self, device_id):
print("stop")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.av_transport.stop()
return "Ok"
return "Error"
[docs] def xmlrpc_next(self, device_id):
print("next")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
next(client.av_transport)
return "Ok"
return "Error"
[docs] def xmlrpc_previous(self, device_id):
print("previous")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.av_transport.previous()
return "Ok"
return "Error"
[docs] def xmlrpc_set_av_transport_uri(self, device_id, uri):
print("set_av_transport_uri")
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.av_transport.set_av_transport_uri(current_uri=uri)
return "Ok"
return "Error"
[docs] def xmlrpc_create_object(self, device_id, container_id, arguments):
print("create_object", arguments)
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.content_directory.create_object(container_id, arguments)
return "Ok"
return "Error"
[docs] def xmlrpc_import_resource(self, device_id, source_uri, destination_uri):
print("import_resource", source_uri, destination_uri)
device = self.control_point.get_device_with_id(device_id)
if device is not None:
client = device.get_client()
client.content_directory.import_resource(source_uri,
destination_uri)
return "Ok"
return "Error"
[docs] def xmlrpc_put_resource(self, url, path):
print("put_resource", url, path)
self.control_point.put_resource(url, path)
return "Ok"
[docs] def xmlrpc_ping(self):
print("ping")
return "Ok"
[docs]def startXMLRPC(control_point, port):
from twisted.web import server
r = XMLRPC(control_point)
print("XMLRPC-API on port %d ready" % port)
reactor.listenTCP(port, server.Site(r))
if __name__ == '__main__':
config = {}
config['logmode'] = 'warning'
config['serverport'] = 30020
from coherence.base import Coherence
ctrl = ControlPoint(Coherence(config),
auto_client=[])
def show_devices():
print("show_devices")
for d in ctrl.get_devices():
print(d, d.get_id())
def the_result(r):
print("result", r, r.get_id())
def query_devices():
print("query_devices")
ctrl.add_query(DeviceQuery('host', '192.168.0.1', the_result))
def query_devices2():
print("query_devices with timeout")
ctrl.add_query(
DeviceQuery('host', '192.168.0.1', the_result, timeout=10,
oneshot=False))
def stop_reactor(*args):
reactor.stop()
print("Stoped reactor successfully")
reactor.callLater(2, show_devices)
reactor.callLater(3, query_devices)
reactor.callLater(4, query_devices2)
reactor.callLater(5, ctrl.add_query, DeviceQuery(
'friendly_name', 'Coherence Test Content',
the_result, timeout=10, oneshot=False))
reactor.callLater(6, stop_reactor)
reactor.run()