def __init__(self, hostname, port=49000, protocol="http"): """Initialize the object. :param str hostname: hostname or IP address of the device :param int port: there is no default port usually, it is different per vendor. Default port for fritz.box is 49000 and when encrypted 49443 :param str protocol: protocol is either http or https :rtype: Lan """ DeviceTR64.__init__(self, hostname, port, protocol)
def __init__(self, hostname, port=49000, protocol="http"): """Initialize the object. :param str hostname: hostname or IP address of the device :param int port: there is no default port usually, it is different per vendor. Default port for fritz.box is 49000 and when encrypted 49443 :param str protocol: protocol is either http or https :rtype: Wifi """ DeviceTR64.__init__(self, hostname, port, protocol)
def __init__(self, hostname, port=49000, protocol="http", verify=True): """Initialize the object. :param str hostname: hostname or IP address of the device :param int port: there is no default port usually, it is different per vendor. Default port for fritz.box is 49000 and when encrypted 49443 :param str protocol: protocol is either http or https :param verify: Whether to verify the SSL certificate of the server, or the path of a certificate file (passed to the verify parameter of requests.get / requests.post :rtype: Lan """ DeviceTR64.__init__(self, hostname, port, protocol, verify)
def test_InitalizeSCPD(self): results = Discover.discover(retries=2) self.assertTrue(len(results) > 0, "No UPnP host found at all.") # setup proxies for discovery call proxies = {} if defaults.test_httpsProxy: proxies = {"https": defaults.test_httpsProxy} if defaults.test_httpProxy: proxies = {"http": defaults.test_httpProxy} # pick the best device in the result list for later loads bestResult = None for result in results: if Discover.rateServiceTypeInResult( result) > Discover.rateServiceTypeInResult(bestResult): bestResult = result # find the device again result = Discover.discoverParticularHost(bestResult.locationHost, proxies=proxies, retries=1) self.assertTrue(result is not None, "Failed to discover: " + bestResult.locationHost) box = DeviceTR64(result.locationHost, result.locationPort, result.locationProtocol) box.username = defaults.test_user box.password = defaults.test_pw box.httpProxy = defaults.test_httpProxy box.httpsProxy = defaults.test_httpsProxy # the discovery result contains the right URL to initialize device definitions box.loadDeviceDefinitions(result.location) # load the actions box.loadSCPD() # the following calls can fail if the device found has not all the definitions needed # self.assertTrue( len(box.deviceServiceDefinitions.keys()) > 0, "Host: " + result.locationHost + " Result used: " + str(result)) self.assertTrue( len(box.deviceInformations.keys()) > 0, "Host: " + result.locationHost + " Result used: " + str(result)) self.assertTrue( len(box.deviceSCPD.keys()) > 0, "Host: " + result.locationHost + " Result used: " + str(result))
}) # resort making sure we start with the important one first sortedResults = sorted(hostResults[host], key=lambda sortit: sortit["sortKey"], reverse=True) # load all xml file definitions for this host loadedXMLFile = [] for sResult in sortedResults: if sResult["result"].location not in loadedXMLFile: loadedXMLFile.append(sResult["result"].location) # get instance of device box = DeviceTR64(sResult["result"].locationHost, sResult["result"].locationPort, sResult["result"].locationProtocol) box.username = use_user box.password = use_pw box.httpProxy = use_httpProxy box.httpsProxy = use_httpsProxy try: # load the device definitions from the location which was in the result box.loadDeviceDefinitions(sResult["result"].location, timeout=use_timeout) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout) as e: # it failed so we will have less service types later pass
if use_httpsProxy: proxies = {"https": use_httpsProxy} if use_httpProxy: proxies = {"http": use_httpProxy} # get TR64 multicast result for the given host to get XML definition url result = Discover.discoverParticularHost(use_host, proxies=proxies, timeout=use_timeout) if not result: raise ValueError("Could not discover given host: " + use_host) # get instance of device box = DeviceTR64.createFromURL(result.location) box.username = use_user box.password = use_pw box.httpProxy = use_httpProxy box.httpsProxy = use_httpsProxy # the discovery result contains the right URL to initialize device definitions box.loadDeviceDefinitions(result.location, timeout=use_timeout) # load the actions box.loadSCPD(timeout=use_timeout, ignoreFailures=True) device = {"informations": box.deviceInformations, "services": {}} if len(box.deviceInformationUnknownKeys.keys()): device["unknownKeys"] = box.deviceInformationUnknownKeys
def test_deviceSetup(self): box = DeviceTR64("some") self.assertEqual(len(box.deviceServiceDefinitions), 0) box.setupTR64Device("fritz.box") self.assertTrue(len(box.deviceServiceDefinitions) > 0)
def test_Defaul(self): box = DeviceTR64("box") self.assertEqual(box.host, "box") self.assertEqual(box.port, 49000) self.assertEqual(box.protocol, "http")
def test_username(self): box = DeviceTR64("some") self.assertEqual(box.username, "") box.username = "******" self.assertEqual(box.username, "abc")
def test_pw(self): box = DeviceTR64("some") self.assertEqual(box.password, "") box.password = "******" self.assertEqual(box.password, "abc123")
# setup proxies for discovery call proxies = {} if use_httpsProxy: proxies = {"https": use_httpsProxy} if use_httpProxy: proxies = {"http": use_httpProxy} # get TR64 multicast result for the given host to get XML definition url result = Discover.discoverParticularHost(use_host, proxies=proxies, timeout=use_timeout) if not result: raise ValueError("Could not discover given host: " + use_host) # get instance of device box = DeviceTR64.createFromURL(result.location) box.username = use_user box.password = use_pw box.httpProxy = use_httpProxy box.httpsProxy = use_httpsProxy # the discovery result contains the right URL to initialize device definitions box.loadDeviceDefinitions(result.location, timeout=use_timeout) # load the actions box.loadSCPD(timeout=use_timeout, ignoreFailures=True) device = {"informations": box.deviceInformations, "services": {}} if len(box.deviceInformationUnknownKeys.keys()): device["unknownKeys"] = box.deviceInformationUnknownKeys
def test_Encryption(self): box = DeviceTR64("some", protocol="https") self.assertEqual(box.host, "some") self.assertEqual(box.port, 49000) self.assertEqual(box.protocol, "https")
def discoverParticularHost(host, service="ssdp:all", deviceDefinitionURL=None, timeout=1, retries=2, ipAddress="239.255.255.250", port=1900, proxies=None): """Discover a particular host and find the best response. This tries to find the most specific discovery result for the given host. Only the discovery result contains the URL to the XML tree which initializes the device definition. If an URL is already known it should be provided to avoid additional latency for a broader first device discovery. This method also do some magic to find the best result for the given host as UPnP devices behave sometimes strangely. This call is costly the result if any should be cached. :param str host: the host to find :param service: the service type or list of service types if known to search for :type service: str or list[str] :param str deviceDefinitionURL: if provided it is used to skip a first device discovery :param float timeout: the time to wait for each retry :param int retries: the amount of times how often the device is tried to discover :param str ipAddress: the multicast ip address to discover devices :param int port: the port to discover devices :param str proxies: proxy definition as defined here: `Proxy definition <http://docs.python-requests.org/en/latest/user/advanced/#proxies>`_ :return: If the device have been found the response is returned otherwise None :rtype: DiscoveryResponse :raises ValueError: if problems with reading or parsing the xml device definition occurs :raises requests.exceptions.ConnectionError: when the device definitions can not be downloaded :raises requests.exceptions.ConnectTimeout: when download time out Example: :: proxies = {"http": "http://localhost:8888"} result = discoverParticularHost("192.168.0.1", proxies=proxies) if result is not None: print("Host: " + result.locationHost + " Port: " + result.locationPort + " Device definitions: " + \\ result.location) .. seealso:: :class:`~simpletr64.DiscoveryResponse`, :meth:`~simpletr64.Discover.discover` """ # get all IP addresses for the given host ipResults = socket.getaddrinfo(host, 80) if len(ipResults) == 0: return None ipAddresses = [] # remember all ip addresses for the given host for ipAdrTupple in ipResults: ipAddresses.append(ipAdrTupple[4][0]) bestPick = None services = [] if deviceDefinitionURL is None: # no xml definition given, so lets search for one # search for all devices first discoverResults = Discover.discover(service=service, timeout=timeout, retries=retries, ipAddress=ipAddress, port=port) for result in discoverResults: if result.locationHost in ipAddresses: # now we found a result for that host, pick the best service type if multiple results for the host # are found if Discover.rateServiceTypeInResult( result) > Discover.rateServiceTypeInResult( bestPick): bestPick = result # remember all services if result.service not in services: services.append(result.service) if bestPick is None: return None else: # create response with given parameter bestPick = DiscoveryResponse.create(deviceDefinitionURL, service=service) # some routers do not advice their TR64 capabilities but their UPnp which is only a subset of actions. # Try to find out if the given XML definition path will give us a better service type. # load xml definition # some devices response differently without a User-Agent headers = {"User-Agent": "Mozilla/5.0; SimpleTR64-3"} request = requests.get(bestPick.location, proxies=proxies, headers=headers, timeout=float(timeout)) if request.status_code != 200: errorStr = DeviceTR64._extractErrorString(request) raise ValueError('Could not get CPE definitions for "' + bestPick.location + '": ' + str(request.status_code) + ' - ' + request.reason + " -- " + errorStr) # parse xml try: root = ET.fromstring(request.text.encode('utf-8')) except Exception as e: raise ValueError("Could not parse CPE definitions for '" + bestPick.location + "': " + str(e)) # find the first deviceType in the document tree for element in root.iter(): # check if element tag name ends on deviceType, skip XML namespace if element.tag.lower().endswith("devicetype"): serviceFound = element.text # remember the service found if it does not exist yet if serviceFound not in services: services.append(serviceFound) # create a specific service just to check if we found it already serviceFound = serviceFound.replace("schemas-upnp-org", "dslforum-org") # test if we already have the best service type then we dont need to do an other discovery request if serviceFound == bestPick.service: return bestPick for service in services: # we search for the specific device tyoe version as of specified in TR64 protocol. # some devices returns different results depending on the given service type, so lets be # very specific specificService = service.replace("schemas-upnp-org", "dslforum-org") if specificService not in services: services.append(specificService) # we do an other discovery request with more specific service/device type discoverResultsSpecific = Discover.discover( service=services, timeout=float(timeout), retries=retries, ipAddress=ipAddress, port=port) # iterate through all results to find the most specific one evenBetterPick = None for specificResult in discoverResultsSpecific: if specificResult.locationHost in ipAddresses: if Discover.rateServiceTypeInResult(specificResult) > \ Discover.rateServiceTypeInResult(evenBetterPick): evenBetterPick = specificResult if evenBetterPick is not None: # best we could find return evenBetterPick # we found first deviceType tag in the XML structure, no need to go further break if deviceDefinitionURL is not None: # we created our own response, so no result found return None # we found only an unspecific result, return it anyway return bestPick
def getCallList(self, timeout=1): """Get the list of phone calls made Example of a phone call result: :: [{'Count': None, 'Name': None, 'CalledNumber': '030868709971', 'Numbertype': 'sip', 'Duration': '0:01', 'Caller': '015155255399', 'Called': 'SIP: 030868729971', 'Date': '02.01.14 13:14', 'Device': 'Anrufbeantworter','Path': None, 'Port': '40', 'Type': '1', 'Id': '15'}] Types: * 1 - answered * 2 - missed * 3 - outgoing :param float timeout: the timeout to wait for the action to be executed :return: the list of made phone calls :rtype: list[dict[str: str]] """ namespace = Fritz.getServiceType("getCallList") uri = self.getControlURL(namespace) results = self.execute(uri, namespace, "GetCallList") # setup proxies proxies = {} if self.httpProxy: proxies = {"https": self.httpProxy} if self.httpsProxy: proxies = {"http": self.httpsProxy} # get the content request = requests.get(results["NewCallListURL"], proxies=proxies, timeout=float(timeout)) if request.status_code != 200: errorStr = DeviceTR64._extractErrorString(request) raise ValueError('Could not get CPE definitions "' + results["NewCallListURL"] + '" : ' + str(request.status_code) + ' - ' + request.reason + " -- " + errorStr) # parse xml try: root = ET.fromstring(request.text.encode('utf-8')) except Exception as e: raise ValueError("Could not parse call list '" + results["NewCallListURL"] + "': " + str(e)) calls = [] for child in root: if child.tag.lower() == "call": callParameters = {} for callChild in child: callParameters[callChild.tag] = callChild.text calls.append(callParameters) return calls
def discoverParticularHost(host, service="ssdp:all", deviceDefinitionURL=None, timeout=1, retries=2, ipAddress="239.255.255.250", port=1900, proxies=None): """Discover a particular host and find the best response. This tries to find the most specific discovery result for the given host. Only the discovery result contains the URL to the XML tree which initializes the device definition. If an URL is already known it should be provided to avoid additional latency for a broader first device discovery. This method also do some magic to find the best result for the given host as UPnP devices behave sometimes strangely. This call is costly the result if any should be cached. :param str host: the host to find :param service: the service type or list of service types if known to search for :type service: str or list[str] :param str deviceDefinitionURL: if provided it is used to skip a first device discovery :param float timeout: the time to wait for each retry :param int retries: the amount of times how often the device is tried to discover :param str ipAddress: the multicast ip address to discover devices :param int port: the port to discover devices :param str proxies: proxy definition as defined here: `Proxy definition <http://docs.python-requests.org/en/latest/user/advanced/#proxies>`_ :return: If the device have been found the response is returned otherwise None :rtype: DiscoveryResponse :raises ValueError: if problems with reading or parsing the xml device definition occurs :raises requests.exceptions.ConnectionError: when the device definitions can not be downloaded :raises requests.exceptions.ConnectTimeout: when download time out Example: :: proxies = {"http": "http://localhost:8888"} result = discoverParticularHost("192.168.0.1", proxies=proxies) if result is not None: print("Host: " + result.locationHost + " Port: " + result.locationPort + " Device definitions: " + \\ result.location) .. seealso:: :class:`~simpletr64.DiscoveryResponse`, :meth:`~simpletr64.Discover.discover` """ # get all IP addresses for the given host ipResults = socket.getaddrinfo(host, 80) if len(ipResults) == 0: return None ipAddresses = [] # remember all ip addresses for the given host for ipAdrTupple in ipResults: ipAddresses.append(ipAdrTupple[4][0]) bestPick = None services = [] if deviceDefinitionURL is None: # no xml definition given, so lets search for one # search for all devices first discoverResults = Discover.discover(service=service, timeout=timeout, retries=retries, ipAddress=ipAddress, port=port) for result in discoverResults: if result.locationHost in ipAddresses: # now we found a result for that host, pick the best service type if multiple results for the host # are found if Discover.rateServiceTypeInResult(result) > Discover.rateServiceTypeInResult(bestPick): bestPick = result # remember all services if result.service not in services: services.append(result.service) if bestPick is None: return None else: # create response with given parameter bestPick = DiscoveryResponse.create(deviceDefinitionURL, service=service) # some routers do not advice their TR64 capabilities but their UPnp which is only a subset of actions. # Try to find out if the given XML definition path will give us a better service type. # load xml definition # some devices response differently without a User-Agent headers = {"User-Agent": "Mozilla/5.0; SimpleTR64-3"} request = requests.get(bestPick.location, proxies=proxies, headers=headers, timeout=float(timeout)) if request.status_code != 200: errorStr = DeviceTR64._extractErrorString(request) raise ValueError('Could not get CPE definitions for "' + bestPick.location + '": ' + str(request.status_code) + ' - ' + request.reason + " -- " + errorStr) # parse xml try: root = ET.fromstring(request.text.encode('utf-8')) except Exception as e: raise ValueError("Could not parse CPE definitions for '" + bestPick.location + "': " + str(e)) # find the first deviceType in the document tree for element in root.getiterator(): # check if element tag name ends on deviceType, skip XML namespace if element.tag.lower().endswith("devicetype"): serviceFound = element.text # remember the service found if it does not exist yet if serviceFound not in services: services.append(serviceFound) # create a specific service just to check if we found it already serviceFound = serviceFound.replace("schemas-upnp-org", "dslforum-org") # test if we already have the best service type then we dont need to do an other discovery request if serviceFound == bestPick.service: return bestPick for service in services: # we search for the specific device tyoe version as of specified in TR64 protocol. # some devices returns different results depending on the given service type, so lets be # very specific specificService = service.replace("schemas-upnp-org", "dslforum-org") if specificService not in services: services.append(specificService) # we do an other discovery request with more specific service/device type discoverResultsSpecific = Discover.discover(service=services, timeout=float(timeout), retries=retries, ipAddress=ipAddress, port=port) # iterate through all results to find the most specific one evenBetterPick = None for specificResult in discoverResultsSpecific: if specificResult.locationHost in ipAddresses: if Discover.rateServiceTypeInResult(specificResult) > \ Discover.rateServiceTypeInResult(evenBetterPick): evenBetterPick = specificResult if evenBetterPick is not None: # best we could find return evenBetterPick # we found first deviceType tag in the XML structure, no need to go further break if deviceDefinitionURL is not None: # we created our own response, so no result found return None # we found only an unspecific result, return it anyway return bestPick
def test_AmountOfHostsConnected(self): data = """<?xml version="10"?> <root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:ms="urn:microsoft-com:wmc-1-0" xmlns:pnpx="http://schemasmicrosoftcom/windows/pnpx/2005/11" xmlns:df="http://schemasmicrosoftcom/windows/2008/09/devicefoundation" xmlns:yamaha="urn:schemas-yamaha-com:device-1-0"> <yamaha:X_device><yamaha:X_URLBase>http://REDACTED:80/</yamaha:X_URLBase><yamaha:X_serviceList><yamaha:X_service><yamaha:X_specType>urn:schemas-yamaha-com:service:X_YamahaRemoteControl:1</yamaha:X_specType><yamaha:X_controlURL>/YamahaRemoteControl/ctrl</yamaha:X_controlURL><yamaha:X_unitDescURL>/YamahaRemoteControl/descxml</yamaha:X_unitDescURL></yamaha:X_service></yamaha:X_serviceList></yamaha:X_device> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <device ms:X_MS_SupportsWMDRM="true"> <dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-150</dlna:X_DLNADOC> <pnpx:X_compatibleId>MS_DigitalMediaDeviceClass_DMR_V001 </pnpx:X_compatibleId> <pnpx:X_deviceCategory>MediaDevices MultimediaDMR MediaDeviceDMC </pnpx:X_deviceCategory> <pnpx:X_hardwareId>VEN_0033&DEV_0006&REV_01 </pnpx:X_hardwareId> <df:X_deviceCategory>MultimediaDMR </df:X_deviceCategory> <deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType> <friendlyName>Pascal</friendlyName> <manufacturer>Yamaha Corporation</manufacturer> <manufacturerURL>http://wwwyamahacom/</manufacturerURL> <modelDescription>AV Receiver</modelDescription> <modelName>RX-V475</modelName> <modelNumber>V475</modelNumber> <modelURL>http://wwwyamahacom/</modelURL> <serialNumber>REDACTED</serialNumber> <UDN>REDACTED</UDN> <UPC>REDACTED</UPC> <iconList> <icon> <mimetype>image/jpeg</mimetype> <width>48</width> <height>48</height> <depth>24</depth> <url>/BCO_device_sm_iconjpg</url> </icon> <icon> <mimetype>image/jpeg</mimetype> <width>120</width> <height>120</height> <depth>24</depth> <url>/BCO_device_lrg_iconjpg</url> </icon> <icon> <mimetype>image/png</mimetype> <width>48</width> <height>48</height> <depth>24</depth> <url>/BCO_device_sm_iconpng</url> </icon> <icon> <mimetype>image/png</mimetype> <width>120</width> <height>120</height> <depth>24</depth> <url>/BCO_device_lrg_iconpng</url> </icon> </iconList> <serviceList> <service> <serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType> <serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId> <SCPDURL>/RenderingControl/descxml</SCPDURL> <controlURL>/RenderingControl/ctrl</controlURL> <eventSubURL>/RenderingControl/evt</eventSubURL> </service> <service> <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType> <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId> <SCPDURL>/ConnectionManager/descxml</SCPDURL> <controlURL>/ConnectionManager/ctrl</controlURL> <eventSubURL>/ConnectionManager/evt</eventSubURL> </service> <service> <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType> <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> <SCPDURL>/AVTransport/descxml</SCPDURL> <controlURL>/AVTransport/ctrl</controlURL> <eventSubURL>/AVTransport/evt</eventSubURL> </service> </serviceList> <presentationURL>http://REDACTED/</presentationURL> </device> </root>""" url = "http://pascalfritzbox:8080/MediaRenderer/desc.xml" box = DeviceTR64.createFromURL(url) box._loadDeviceDefinitions(url, data) self.assertTrue(len(box.deviceServiceDefinitions.keys()) > 0)
def getCallList(self, timeout=1): """Get the list of phone calls made Example of a phone call result: :: [{'Count': None, 'Name': None, 'CalledNumber': '030868709971', 'Numbertype': 'sip', 'Duration': '0:01', 'Caller': '015155255399', 'Called': 'SIP: 030868729971', 'Date': '02.01.14 13:14', 'Device': 'Anrufbeantworter','Path': None, 'Port': '40', 'Type': '1', 'Id': '15'}] Types: * 1 - answered * 2 - missed * 3 - outgoing :param float timeout: the timeout to wait for the action to be executed :return: the list of made phone calls :rtype: bool """ namespace = Fritz.getServiceType("getCallList") uri = self.getControlURL(namespace) results = self.execute(uri, namespace, "GetCallList") # setup proxies proxies = {} if self.httpProxy: proxies = {"https": self.httpProxy} if self.httpsProxy: proxies = {"http": self.httpsProxy} # get the content request = requests.get(results["NewCallListURL"], proxies=proxies, timeout=float(timeout)) if request.status_code != 200: errorStr = DeviceTR64._extractErrorString(request) raise ValueError( 'Could not get CPE definitions "' + results["NewCallListURL"] + '" : ' + str(request.status_code) + " - " + request.reason + " -- " + errorStr ) # parse xml try: root = ET.fromstring(request.text.encode("utf-8")) except Exception as e: raise ValueError("Could not parse call list '" + results["NewCallListURL"] + "': " + str(e)) calls = [] for child in root.getchildren(): if child.tag.lower() == "call": callParameters = {} for callChild in child.getchildren(): callParameters[callChild.tag] = callChild.text calls.append(callParameters) return calls
use_arguments = {} for argument in args.arguments: args = argument.split("::") if len(args) != 2: raise ValueError("Argument needs to be in the format of ArgumentName::Value, found: " + argument) use_arguments[args[0]] = args[1] ####################################################################################################################### urlParts = urlparse(use_controlURL) uri = urlParts.path device = DeviceTR64.createFromURL(use_controlURL) device.username = use_user device.password = use_pw device.httpProxy = use_httpProxy device.httpsProxy = use_httpsProxy try: # where the "magic" happens results = device.execute(uri, use_namespace, use_action, timeout=use_timeout, **use_arguments) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError) as e: print("Failed: " + str(e)) results = {} if len(results.keys()): print("Results:")
def test_Port(self): box = DeviceTR64("box", port=1234) self.assertEqual(box.host, "box") self.assertEqual(box.port, 1234) self.assertEqual(box.protocol, "http")
for argument in args.arguments: args = argument.split("::") if len(args) != 2: raise ValueError( "Argument needs to be in the format of ArgumentName::Value, found: " + argument) use_arguments[args[0]] = args[1] ####################################################################################################################### urlParts = urlparse(use_controlURL) uri = urlParts.path device = DeviceTR64.createFromURL(use_controlURL) device.username = use_user device.password = use_pw device.httpProxy = use_httpProxy device.httpsProxy = use_httpsProxy try: # where the "magic" happens results = device.execute(uri, use_namespace, use_action, timeout=use_timeout, **use_arguments) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError) as e: