docs: def call(self, action_name, arg_in=None, http_auth=None, http_headers=None):
          """
          Construct the XML and make the call to the device. Parse the response values into a dict.
          """
          if arg_in is None:
              arg_in = {}
  
          soap_env = '{%s}' % NS_SOAP_ENV
          m = '{%s}' % self.service_type
  
          root = etree.Element(soap_env+'Envelope', nsmap={'SOAP-ENV': NS_SOAP_ENV})
          root.attrib[soap_env+'encodingStyle'] = ENCODING_STYLE
          body = etree.SubElement(root, soap_env+'Body')
          action = etree.SubElement(body, m+action_name, nsmap={'m': self.service_type})
          for key, value in arg_in.items():
              etree.SubElement(action, key).text = str(value)
          body = etree.tostring(root, encoding=ENCODING, xml_declaration=True)
          headers = {
              'SOAPAction': '"%s#%s"' % (self.service_type, action_name),
              'Host': self._host,
              'Content-Type': 'text/xml',
              'Content-Length': str(len(body)),
          }
          headers.update(http_headers or {})
  
          try:
              resp = requests.post(
                  self.url,
                  body,
                  headers=headers,
                  timeout=SOAP_TIMEOUT,
                  auth=http_auth
              )
              resp.raise_for_status()
          except requests.exceptions.HTTPError as exc:
              # If the body of the error response contains XML then it should be a UPnP error,
              # otherwise reraise the HTTPError.
              try:
                  err_xml = etree.fromstring(exc.response.content)
              except etree.XMLSyntaxError:
                  raise exc
              raise SOAPError(*self._extract_upnperror(err_xml))
  
          xml_str = resp.content.strip()
          try:
              xml = etree.fromstring(xml_str)
          except etree.XMLSyntaxError:
              # Try removing any extra XML declarations in case there are more than one.
              # This sometimes happens when a device sends its own XML config files.
              xml = etree.fromstring(self._remove_extraneous_xml_declarations(xml_str))
          except ValueError:
              # This can occur when requests returns a `str` (unicode) but there's also an XML
              # declaration, which lxml doesn't like.
              xml = etree.fromstring(xml_str.encode('utf8'))
  
          response = xml.find(".//{%s}%sResponse" % (self.service_type, action_name))
          if response is None:
              msg = ('Returned XML did not include an element which matches namespace %r and tag name'
                     ' \'%sResponse\'.' % (self.service_type, action_name))
              self._log.debug(msg + '\n' + etree.tostring(xml, pretty_print=True).decode('utf8'))
              raise SOAPProtocolError(msg)
  
          # Sometimes devices return XML strings as their argument values without escaping them with
          # CDATA. This checks to see if the argument has been parsed as XML and un-parses it if so.
          ret = {}
          for arg in response.getchildren():
              children = arg.getchildren()
              if children:
                  ret[arg.tag] = b"\n".join(etree.tostring(x) for x in children)
              else:
                  ret[arg.tag] = arg.text
  
          return ret