text: import os
  import threading
  import json
  import ssl
  import urllib.request
  import urllib.parse
  import xml.etree.ElementTree as ET
  from xmlrpc.server import SimpleXMLRPCServer
  from xmlrpc.server import SimpleXMLRPCRequestHandler
  import xmlrpc.client
  import socket
  #from socketserver import ThreadingMixIn
  import logging
  
  from pyhomematic import devicetypes
  from pyhomematic.devicetypes.generic import HMChannel
  
  LOG = logging.getLogger(__name__)
  
  # Constants
  LOCAL = '0.0.0.0'
  LOCALPORT = 0
  DEFAULT_JSONPORT = 80
  REMOTES = {
      'default': {
          'ip': '127.0.0.1',
          'port': 2001,
          'path': '',
          'username': 'Admin',
          'password': '',
          'jsonport': DEFAULT_JSONPORT,
          'resolvenames': False,
          'connect': True,
      }}
  DEVICEFILE = None  # e.g. devices_%s.json
  PARAMSETFILE = None # e.g. paramsets_%s.json
  INTERFACE_ID = 'pyhomematic'
  XML_API_URL = '/config/xmlapi/devicelist.cgi'
  JSONRPC_URL = '/api/homematic.cgi'
  BACKEND_UNKNOWN = 0
  BACKEND_CCU = 1
  BACKEND_HOMEGEAR = 2
  WORKING = False
  
  
  # Device-storage
  devices = {}
  devices_all = {}
  devices_raw = {}
  devices_raw_dict = {}
  paramsets = {}
  
  
  def make_http_credentials(username=None, password=None):
      """Build auth part for api_url."""
      credentials = ''
      if username is None:
          return credentials
      if username is not None:
          if ':' in username:
              return credentials
          credentials += username
      if credentials and password is not None:
          credentials += ":%s" % password
      return "%s@" % credentials
  
  
  def build_api_url(host=REMOTES['default']['ip'],
                    port=REMOTES['default']['port'],
                    path=REMOTES['default']['path'],
                    username=None,
                    password=None,
                    ssl=False):
      """Build API URL from components."""
      credentials = make_http_credentials(username, password)
      scheme = 'http'
      if not path:
          path = ''
      if path and not path.startswith('/'):
          path = "/%s" % path
      if ssl:
          scheme += 's'
      return "%s://%s%s:%i%s" % (scheme, credentials, host, port, path)
  
  
  # Object holding the methods the XML-RPC server should provide.
  class RPCFunctions():
  
      def __init__(self,
                   devicefile=DEVICEFILE,
                   paramsetfile=PARAMSETFILE,
                   proxies={},
                   remotes={},
                   eventcallback=False,
                   systemcallback=False,
                   resolveparamsets=False):
          global devices, devices_all, devices_raw, devices_raw_dict, paramsets
          LOG.debug("RPCFunctions.__init__")
          self.devicefile = None
          if devicefile is not None:
              if "%s" in devicefile:
                  self.devicefile = devicefile
              else:
                  LOG.warning("RPCFunctions.__init__: Invalid devicefile template")
                  self.devicefile = None
          self.paramsetfile = None
          if paramsetfile is not None:
              if "%s" in paramsetfile:
                  self.paramsetfile = paramsetfile
              else:
                  LOG.warning("RPCFunctions.__init__: Invalid paramsetfile template")
                  self.paramsetfile = None
          self.eventcallback = eventcallback
          self.systemcallback = systemcallback
          self.resolveparamsets = resolveparamsets
          self.remotes = remotes
          self._paramsets = paramsets
  
          # The methods need to know about the proxyies to be able to pass it on
          # to the device-objects
          self._proxies = proxies
  
          # Devices w/o channels will be accessible using the device-address as
          # the key
          self.devices = devices
          # Devices including channels will be accessible using the
          # device-address + channel as the key
          self.devices_all = devices_all
  
          # The plain JSON (actually dicts) are stored as well
          self._devices_raw_dict = devices_raw_dict
          self._devices_raw = devices_raw
  
          for interface_id in proxies:
              LOG.debug("RPCFunctions.__init__: iterating proxy = %s", interface_id)
              remote = interface_id.split('-')[-1]
              self.devices[remote] = {}
              self.devices_all[remote] = {}
              self._devices_raw[remote] = []
              self._devices_raw_dict[remote] = {}
              self._paramsets[remote] = {}
  
              # If there are stored devices, we load them instead of getting them
              # from the server.
              if self.devicefile is not None:
                  devicefilename = self.devicefile % remote
                  LOG.debug("RPCFunctions.__init__: devicefile = %s", devicefilename)
                  if os.path.isfile(devicefilename):
                      with open(devicefilename, 'r') as fptr:
                          fcontent = fptr.read()
                          if fcontent:
                              self._devices_raw[remote] = json.loads(fcontent)
  
              # Load stored paramsets if available
              if self.paramsetfile is not None:
                  paramsetfilename = self.paramsetfile % remote
                  LOG.debug("RPCFunctions.__init__: paramsetfile = %s", paramsetfilename)
                  if os.path.isfile(paramsetfilename):
                      with open(paramsetfilename, 'r') as fptr:
                          fcontent = fptr.read()
                          if fcontent:
                              self._paramsets[remote] = json.loads(fcontent)
  
              # Continue if there are no stored devices
              if not self._devices_raw.get(remote):
                  continue
              for device in self._devices_raw[remote]:
                  self._devices_raw_dict[remote][device['ADDRESS']] = device
              LOG.debug("RPCFunctions.__init__: devices_raw = %s" %
                        (str(self._devices_raw[remote]), ))
  
              # Create the "interactive" device-objects from cache and store
              # them in self._devices and self._devices_all
              self.createDeviceObjects(interface_id)
  
      def createDeviceObjects(self, interface_id):
          """Transform the raw device descriptions into instances of devicetypes.generic.HMDevice or availabe subclass."""
          global WORKING
          WORKING = True
          remote = interface_id.split('-')[-1]
          LOG.debug(
              "RPCFunctions.createDeviceObjects: iterating interface_id = %s", remote)
          # First create parent object
          for dev in self._devices_raw[remote]:
              if not dev['PARENT']:
                  if dev['ADDRESS'] not in self.devices_all[remote]:
                      try:
                          if dev['TYPE'] in devicetypes.SUPPORTED:
                              deviceObject = devicetypes.SUPPORTED[dev['TYPE']](
                                  dev, self._proxies[interface_id], self.resolveparamsets)
                              LOG.debug("RPCFunctions.createDeviceObjects: created %s  as SUPPORTED device for %s" % (
                                  dev['ADDRESS'], dev['TYPE']))
                          else:
                              deviceObject = devicetypes.UNSUPPORTED(
                                  dev, self._proxies[interface_id], self.resolveparamsets)
                              LOG.debug("RPCFunctions.createDeviceObjects: created %s  as UNSUPPORTED device for %s" % (
                                  dev['ADDRESS'], dev['TYPE']))
                          LOG.debug(
                              "RPCFunctions.createDeviceObjects: adding to self.devices_all")
                          self.devices_all[remote][dev['ADDRESS']] = deviceObject
                          LOG.debug(
                              "RPCFunctions.createDeviceObjects: adding to self.devices")
                          self.devices[remote][dev['ADDRESS']] = deviceObject
                      except Exception as err:
                          LOG.critical(
                              "RPCFunctions.createDeviceObjects: Parent: %s", str(err))
          # Then create all children for parent
          for dev in self._devices_raw[remote]:
              if dev['PARENT']:
                  try:
                      if dev['ADDRESS'] not in self.devices_all[remote]:
                          deviceObject = HMChannel(
                              dev, self._proxies[interface_id], self.resolveparamsets)
                          self.devices_all[remote][dev['ADDRESS']] = deviceObject
                          self.devices[remote][dev['PARENT']].CHANNELS[
                              dev['INDEX']] = deviceObject
                  except Exception as err:
                      LOG.critical(
                          "RPCFunctions.createDeviceObjects: Child: %s", str(err))
          if self.devices_all[remote] and self.remotes[remote].get('resolvenames', False):
              self.addDeviceNames(remote)
          WORKING = False
          if self.systemcallback:
              self.systemcallback('createDeviceObjects')
          return True
  
      def error(self, interface_id, errorcode, msg):
          """When some error occurs the CCU / Homegear will send it's error message here"""
          LOG.debug("RPCFunctions.error: interface_id = %s, errorcode = %i, message = %s",
                    interface_id, int(errorcode), str(msg))
          if self.systemcallback:
              self.systemcallback('error', interface_id, errorcode, msg)
          return True
  
      def saveDevices(self, remote):
          """We save known devices into a json-file so we don't have to work through the whole list of devices the CCU / Homegear presents us"""
          if self.devicefile is not None:
              devicefilename = self.devicefile % remote
              LOG.debug("RPCFunctions.saveDevices: devicefile: %s", devicefilename)
              try:
                  with open(devicefilename, 'w') as df:
                      df.write(json.dumps(self._devices_raw[remote]))
                  return True
              except Exception as err:
                  LOG.warning(
                      "RPCFunctions.saveDevices: Exception saving _devices_raw: %s", str(err))
                  return False
          else:
              return True
  
      def saveParamsets(self, remote):
          """Write known paramsets to disk."""
          if self.paramsetfile is not None:
              paramsetfilename = self.paramsetfile % remote
              LOG.debug("RPCFunctions.saveParamsets: paramsetfile: %s", paramsetfilename)
              try:
                  with open(paramsetfilename, 'w') as df:
                      df.write(json.dumps(self._paramsets[remote]))
                  return True
              except Exception as err:
                  LOG.warning(
                      "RPCFunctions.saveParamsets: Exception saving _paramsets: %s", str(err))
                  return False
  
      def event(self, interface_id, address, value_key, value):
          """If a device emits some sort event, we will handle it here."""
          LOG.debug("RPCFunctions.event: interface_id = %s, address = %s, value_key = %s, value = %s" % (
              interface_id, address, value_key.upper(), str(value)))
          self.devices_all[interface_id.split(
              '-')[-1]][address].event(interface_id, value_key.upper(), value)
          if self.eventcallback:
              self.eventcallback(interface_id=interface_id, address=address,
                                 value_key=value_key.upper(), value=value)
          return True
  
      def listDevices(self, interface_id):
          """The CCU / Homegear asks for devices known to our XML-RPC server. We respond to that request using this method."""
          LOG.debug("RPCFunctions.listDevices: interface_id = %s, _devices_raw = %s" % (
              interface_id, str(self._devices_raw)))
          remote = interface_id.split('-')[-1]
          if remote not in self._devices_raw:
              self._devices_raw[remote] = []
          if self.systemcallback:
              self.systemcallback('listDevices', interface_id)
          return self._devices_raw[remote]
  
      def newDevices(self, interface_id, dev_descriptions):
          """The CCU / Homegear informs us about newly added devices. We react on that and add those devices as well."""
          LOG.debug("RPCFunctions.newDevices: interface_id = %s, dev_descriptions = %s" % (
              interface_id, str(dev_descriptions)))
          remote = interface_id.split('-')[-1]
          if remote not in self._devices_raw:
              self._devices_raw[remote] = []
          if remote not in self._devices_raw_dict:
              self._devices_raw_dict[remote] = {}
          if remote not in self._paramsets:
              self._paramsets[remote] = {}
          for d in dev_descriptions:
              self._devices_raw[remote].append(d)
              self._devices_raw_dict[remote][d['ADDRESS']] = d
              self._paramsets[remote][d['ADDRESS']] = {}
          self.saveDevices(remote)
          self.saveParamsets(remote)
          self.createDeviceObjects(interface_id)
          if self.systemcallback:
              self.systemcallback('newDevices', interface_id, dev_descriptions)
          return True
  
      def deleteDevices(self, interface_id, addresses):
          """The CCU / Homegear informs us about removed devices. We react on that and remove those devices as well."""
          LOG.debug("RPCFunctions.deleteDevices: interface_id = %s, addresses = %s" % (
              interface_id, str(addresses)))
          # TODO: remove known device objects as well
          remote = interface_id.split('-')[-1]
          self._devices_raw[remote] = [device for device in self._devices_raw[
              remote] if not device['ADDRESS'] in addresses]
          self.saveDevices(remote)
          for address in addresses:
              try:
                  del self._paramsets[remote][address]
              except KeyError:
                  pass
          self.saveParamsets(remote)
          if self.systemcallback:
              self.systemcallback('deleteDevice', interface_id, addresses)
          return True
  
      def updateDevice(self, interface_id, address, hint):
          LOG.debug("RPCFunctions.updateDevice: interface_id = %s, address = %s, hint = %s" % (
              interface_id, address, str(hint)))
          # TODO: Implement updateDevice
          if self.systemcallback:
              self.systemcallback('updateDevice', interface_id, address, hint)
          return True
  
      def replaceDevice(self, interface_id, oldDeviceAddress, newDeviceAddress):
          LOG.debug("RPCFunctions.replaceDevice: interface_id = %s, oldDeviceAddress = %s, newDeviceAddress = %s" % (
              interface_id, oldDeviceAddress, newDeviceAddress))
          # TODO: Implement replaceDevice
          if self.systemcallback:
              self.systemcallback('replaceDevice', interface_id,
                                  oldDeviceAddress, newDeviceAddress)
          return True
  
      def readdedDevice(self, interface_id, addresses):
          LOG.debug("RPCFunctions.readdedDevices: interface_id = %s, addresses = %s" % (
              interface_id, str(addresses)))
          # TODO: Implement readdedDevice
          if self.systemcallback:
              self.systemcallback('readdedDevice', interface_id, addresses)
          return True
  
      def jsonRpcPost(self, host, jsonport, method, params={}):
          LOG.debug("RPCFunctions.jsonRpcPost: Method: %s" % method)
          try:
              payload = json.dumps(
                  {"method": method, "params": params, "jsonrpc": "1.1", "id": 0}).encode('utf-8')
  
              headers = {"Content-Type": 'application/json',
                         "Content-Length": len(payload)}
              apiendpoint = "http://%s:%s%s" % (host, jsonport, JSONRPC_URL)
              LOG.debug("RPCFunctions.jsonRpcPost: API-Endpoint: %s" %
                        apiendpoint)
              req = urllib.request.Request(apiendpoint, payload, headers)
              resp = urllib.request.urlopen(req)
              if resp.status == 200:
                  try:
                      return json.loads(resp.read().decode('utf-8'))
                  except ValueError as err:
                      # Workaround for bug in CCU
                      return json.loads(resp.read().decode('utf-8').replace("\\", ""))
              else:
                  LOG.error("RPCFunctions.jsonRpcPost: Status: %i" % resp.status)
                  return {'error': resp.status, 'result': {}}
          except Exception as err:
              LOG.error("RPCFunctions.jsonRpcPost: Exception: %s" % str(err))
              return {'error': str(err), 'result': {}}
  
      def addDeviceNames(self, remote):
          """ If XML-API (http://www.homematic-inside.de/software/addons/item/xmlapi) is installed on CCU this function will add names to CCU devices """
          LOG.debug("RPCFunctions.addDeviceNames")
  
          # First try to get names from metadata when nur credentials are set
          if self.remotes[remote]['resolvenames'] == 'metadata':
              for address in self.devices[remote]:
                  try:
                      name = self.devices[remote][
                          address]._proxy.getMetadata(address, 'NAME')
                      self.devices[remote][address].NAME = name
                      for address, device in self.devices[remote][address].CHANNELS.items():
                          device.NAME = name
                          self.devices_all[remote][device.ADDRESS].NAME = name
                  except Exception as err:
                      LOG.debug(
                          "RPCFunctions.addDeviceNames: Unable to get name for %s from metadata." % str(address))
  
          # Then try to get names via JSON-RPC
          elif (self.remotes[remote]['resolvenames'] == 'json' and
                self.remotes[remote]['username'] and
                self.remotes[remote]['password']):
              LOG.debug("RPCFunctions.addDeviceNames: Getting names via JSON-RPC")
              try:
                  session = False
                  params = {"username": self.remotes[remote][
                      'username'], "password": self.remotes[remote]['password']}
                  response = self.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.login", params)
                  if response['error'] is None and response['result']:
                      session = response['result']
  
                  if not session:
                      LOG.warning(
                          "RPCFunctions.addDeviceNames: Unable to open session.")
                      return
  
                  params = {"_session_id_": session}
                  response = self.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Interface.listInterfaces", params)
                  interface = False
                  if response['error'] is None and response['result']:
                      for i in response['result']:
                          if i['port'] in [self.remotes[remote]['port'], self.remotes[remote]['port'] + 30000]:
                              interface = i['name']
                              break
                  LOG.debug(
                      "RPCFunctions.addDeviceNames: Got interface: %s" % interface)
                  if not interface:
                      params = {"_session_id_": session}
                      response = self.jsonRpcPost(
                          self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.logout", params)
                      return
  
                  params = {"_session_id_": session}
                  response = self.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Device.listAllDetail", params)
  
                  if response['error'] is None and response['result']:
                      LOG.debug(
                          "RPCFunctions.addDeviceNames: Resolving devicenames")
                      for i in response['result']:
                          try:
                              if i.get('address') in self.devices[remote]:
                                  self.devices[remote][
                                      i['address']].NAME = i['name']
                          except Exception as err:
                              LOG.warning(
                                  "RPCFunctions.addDeviceNames: Exception: %s" % str(err))
  
                  params = {"_session_id_": session}
                  response = self.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.logout", params)
              except Exception as err:
                  params = {"_session_id_": session}
                  response = self.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.logout", params)
                  LOG.warning(
                      "RPCFunctions.addDeviceNames: Exception: %s" % str(err))
  
          # Then try to get names from XML-API
          elif self.remotes[remote]['resolvenames'] == 'xml':
              LOG.warning("Resolving names with the XML-API addon will be disabled in a future release. Please switch to json.")
              try:
                  response = urllib.request.urlopen(
                      "http://%s%s" % (self.remotes[remote]['ip'], XML_API_URL), timeout=5)
                  device_list = response.read().decode("ISO-8859-1")
              except Exception as err:
                  LOG.warning(
                      "RPCFunctions.addDeviceNames: Could not access XML-API: %s" % (str(err), ))
                  return
              device_list_tree = ET.ElementTree(ET.fromstring(device_list))
              for device in device_list_tree.getroot():
                  address = device.attrib['address']
                  name = device.attrib['name']
                  if address in self.devices[remote]:
                      self.devices[remote][address].NAME = name
                      for address, device in self.devices[remote][address].CHANNELS.items():
                          device.NAME = name
                          self.devices_all[remote][device.ADDRESS].NAME = name
  
  
  class LockingServerProxy(xmlrpc.client.ServerProxy):
      """
      ServerProxy implementation with lock when request is executing
      """
  
      def __init__(self, *args, **kwargs):
          """
          Initialize new proxy for server and get local ip
          """
          self._remote = kwargs.pop("remote", None)
          self._skipinit = kwargs.pop("skipinit", False)
          self._callbackip = kwargs.pop("callbackip", None)
          self._callbackport = kwargs.pop("callbackport", None)
          self._ssl = kwargs.pop("ssl", False)
          self._verify_ssl = kwargs.pop("verify_ssl", True)
          self.lock = threading.Lock()
          if self._ssl and not self._verify_ssl and self._verify_ssl is not None:
              kwargs['context'] = ssl._create_unverified_context()
          xmlrpc.client.ServerProxy.__init__(self, encoding="ISO-8859-1", *args, **kwargs)
          urlcomponents = urllib.parse.urlparse(args[0])
          self._remoteip = urlcomponents.hostname
          self._remoteport = urlcomponents.port
          LOG.debug("LockingServerProxy.__init__: Getting local ip")
          tmpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
          tmpsocket.connect((self._remoteip, self._remoteport))
          self._localip = tmpsocket.getsockname()[0]
          tmpsocket.close()
          LOG.debug("LockingServerProxy.__init__: Got local ip %s" %
                    self._localip)
  
      def __request(self, *args, **kwargs):
          """
          Call method on server side
          """
  
          with self.lock:
              parent = xmlrpc.client.ServerProxy
              # pylint: disable=E1101
              return parent._ServerProxy__request(self, *args, **kwargs)
  
      def __getattr__(self, *args, **kwargs):
          """
          Magic method dispatcher
          """
  
          return xmlrpc.client._Method(self.__request, *args, **kwargs)
  
  # Restrict to particular paths.
  
  
  class RequestHandler(SimpleXMLRPCRequestHandler):
      """We handle requests to / and /RPC2"""
      rpc_paths = ('/', '/RPC2',)
  
  
  # pylint: disable=too-many-public-methods
  class ServerThread(threading.Thread):
      """XML-RPC server thread to handle messages from CCU / Homegear"""
  
      def __init__(self,
                   local=LOCAL,
                   localport=LOCALPORT,
                   remotes=REMOTES,
                   devicefile=DEVICEFILE,
                   paramsetfile=PARAMSETFILE,
                   interface_id=INTERFACE_ID,
                   eventcallback=False,
                   systemcallback=False,
                   resolveparamsets=False):
          LOG.debug("ServerThread.__init__")
          threading.Thread.__init__(self)
  
          # Member
          self._interface_id = interface_id
          self._local = local
          self._localport = int(localport)
          self._devicefile = devicefile
          self._paramsetfile = paramsetfile
          self.remotes = remotes
          self.eventcallback = eventcallback
          self.systemcallback = systemcallback
          self.resolveparamsets = resolveparamsets
          self.proxies = {}
          self.failed_inits = []
  
          self.createProxies()
          if not self.proxies:
              LOG.warning("No proxies available. Aborting.")
              raise Exception
  
          self._rpcfunctions = RPCFunctions(devicefile=self._devicefile,
                                            paramsetfile=self._paramsetfile,
                                            proxies=self.proxies,
                                            remotes=self.remotes,
                                            eventcallback=self.eventcallback,
                                            systemcallback=self.systemcallback,
                                            resolveparamsets=self.resolveparamsets)
  
          # Setup server to handle requests from CCU / Homegear
          LOG.debug("ServerThread.__init__: Setting up server")
          # class SimpleThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
          #     pass
          # self.server = SimpleThreadedXMLRPCServer((self._local, self._localport),
          #                                          requestHandler=RequestHandler,
          #                                          logRequests=False)
          self.server = SimpleXMLRPCServer((self._local, self._localport),
                                           requestHandler=RequestHandler,
                                           logRequests=False)
          self._localport = self.server.socket.getsockname()[1]
          self.server.register_introspection_functions()
          self.server.register_multicall_functions()
          LOG.debug("ServerThread.__init__: Registering RPC functions")
          self.server.register_instance(
              self._rpcfunctions, allow_dotted_names=True)
  
      def run(self):
          LOG.info("Starting server at http://%s:%i" %
                   (self._local, self._localport))
          self.server.serve_forever()
  
      def createProxies(self):
          """Create proxies to interact with CCU / Homegear"""
          LOG.debug("createProxies: Creating proxies")
          for remote, host in self.remotes.items():
              # Initialize XML-RPC
              try:
                  socket.gethostbyname(host['ip'])
              except Exception as err:
                  LOG.warning("Skipping proxy: %s" % str(err))
                  continue
              if 'path' not in host:
                  host['path'] = ''
              LOG.info("Creating proxy %s. Connecting to %s:%i%s" %
                       (remote, host['ip'], host['port'], host['path']))
              host['id'] = "%s-%s" % (self._interface_id, remote)
              try:
                  api_url = build_api_url(host=host['ip'],
                                          port=host['port'],
                                          path=host['path'],
                                          username=host.get('username'),
                                          password=host.get('password'),
                                          ssl=host.get('ssl'))
                  self.proxies[host['id']] = LockingServerProxy(
                      api_url,
                      remote=remote,
                      callbackip=host.get('callbackip', None),
                      callbackport=host.get('callbackport', None),
                      skipinit=not host.get('connect', True),
                      ssl=host.get('ssl', False),
                      verify_ssl=host.get('verify_ssl', True))
              except Exception as err:
                  LOG.warning("Failed connecting to proxy at http://%s:%i%s" %
                              (host['ip'], host['port'], host['path']))
                  LOG.debug("__init__: Exception: %s" % str(err))
                  raise Exception
              try:
                  host['type'] = BACKEND_UNKNOWN
                  #if "Homegear" in self.proxies[host['id']].getVersion():
                  #    LOG.debug("__init__: Host is Homegear")
                  #    host['type'] = BACKEND_HOMEGEAR
                  #else:
                  #    LOG.debug("__init__: Host is CCU")
                  #    host['type'] = BACKEND_CCU
              except Exception as err:
                  LOG.warning("__init__: Failed to detect backend type: %s" % str(err))
                  host['type'] = BACKEND_UNKNOWN
  
      def clearProxies(self):
          """Remove existing proxy objects."""
          LOG.debug("clearProxies: Clearing proxies")
          self.proxies.clear()
  
      def proxyInit(self):
          """
          To receive events the proxy has to tell the CCU / Homegear where to send the events. For that we call the init-method.
          """
          # Call init() with local XML RPC config and interface_id (the name of
          # the receiver) to receive events. XML RPC server has to be running.
          for interface_id, proxy in self.proxies.items():
              if proxy._skipinit:
                  LOG.warning("Skipping init for %s", interface_id)
                  continue
              if proxy._callbackip and proxy._callbackport:
                  callbackip = proxy._callbackip
                  callbackport = proxy._callbackport
              else:
                  callbackip = proxy._localip
                  callbackport = self._localport
              LOG.debug("ServerThread.proxyInit: init('http://%s:%i', '%s')" %
                        (callbackip, callbackport, interface_id))
              try:
                  proxy.init("http://%s:%i" %
                             (callbackip, callbackport), interface_id)
                  LOG.info("Proxy for %s initialized", interface_id)
              except Exception as err:
                  LOG.debug("proxyInit: Exception: %s" % str(err))
                  LOG.warning("Failed to initialize proxy for %s", interface_id)
                  self.failed_inits.append(interface_id)
  
      def proxyDeInit(self):
          """De-Init from the proxies."""
          stopped = []
          for interface_id, proxy in self.proxies.items():
              if interface_id in self.failed_inits:
                  LOG.warning("ServerThread.proxyDeInit: Not performing de-init for %s", interface_id)
                  continue
              if proxy._callbackip and proxy._callbackport:
                  callbackip = proxy._callbackip
                  callbackport = proxy._callbackport
              else:
                  callbackip = proxy._localip
                  callbackport = self._localport
              remote = "http://%s:%i" % (callbackip, callbackport)
              LOG.debug("ServerThread.proxyDeInit: init('%s')", remote)
              if not interface_id in stopped:
                  try:
                      proxy.init(remote)
                      stopped.append(interface_id)
                      LOG.info("proxyDeInit: Proxy for %s de-initialized: %s", interface_id, remote)
                  except Exception as err:
                      LOG.debug("proxyDeInit: Exception: %s", err)
                      LOG.warning("proxyDeInit: Failed to de-initialize proxy")
  
      def stop(self):
          """To stop the server we de-init from the CCU / Homegear, then shut down our XML-RPC server."""
          self.proxyDeInit()
          self.clearProxies()
          LOG.info("Shutting down server")
          self.server.shutdown()
          LOG.debug("ServerThread.stop: Stopping ServerThread")
          self.server.server_close()
          LOG.info("HomeMatic XML-RPC Server stopped")
  
      def parseCCUSysVar(self, data):
          """Helper to parse type of system variables of CCU"""
          if data['type'] == 'LOGIC':
              return data['name'], data['value'] == 'true'
          elif data['type'] == 'NUMBER':
              return data['name'], float(data['value'])
          elif data['type'] == 'LIST':
              return data['name'], int(data['value'])
          else:
              return data['name'], data['value']
  
      def jsonRpcLogin(self, remote):
          """Login to CCU and return session"""
          session = False
          try:
              params = {"username": self.remotes[remote][
                  'username'], "password": self.remotes[remote]['password']}
              response = self._rpcfunctions.jsonRpcPost(
                  self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.login", params)
              if response['error'] is None and response['result']:
                  session = response['result']
  
              if not session:
                  LOG.warning(
                      "ServerThread.jsonRpcLogin: Unable to open session.")
          except Exception as err:
              LOG.debug(
                  "ServerThread.jsonRpcLogin: Exception while logging in via JSON-RPC: %s" % str(err))
          return session
  
      def jsonRpcLogout(self, remote, session):
          """Logout of CCU"""
          logout = False
          try:
              params = {"_session_id_": session}
              response = self._rpcfunctions.jsonRpcPost(
                  self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "Session.logout", params)
              if response['error'] is None and response['result']:
                  logout = response['result']
          except Exception as err:
              LOG.debug(
                  "ServerThread.jsonRpcLogout: Exception while logging in via JSON-RPC: %s" % str(err))
          return logout
  
      def getAllSystemVariables(self, remote):
          """Get all system variables from CCU / Homegear"""
          variables = {}
          if self.remotes[remote]['username'] and self.remotes[remote]['password']:
              LOG.debug(
                  "ServerThread.getAllSystemVariables: Getting all System variables via JSON-RPC")
              session = self.jsonRpcLogin(remote)
              if not session:
                  return
              try:
                  params = {"_session_id_": session}
                  response = self._rpcfunctions.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "SysVar.getAll", params)
                  if response['error'] is None and response['result']:
                      for var in response['result']:
                          key, value = self.parseCCUSysVar(var)
                          variables[key] = value
  
                  self.jsonRpcLogout(remote, session)
              except Exception as err:
                  self.jsonRpcLogout(remote, session)
                  LOG.warning(
                      "ServerThread.getAllSystemVariables: Exception: %s" % str(err))
          else:
              try:
                  variables = self.proxies[
                      "%s-%s" % (self._interface_id, remote)].getAllSystemVariables()
              except Exception as err:
                  LOG.debug(
                      "ServerThread.getAllSystemVariables: Exception: %s" % str(err))
          return variables
  
      def getSystemVariable(self, remote, name):
          """Get single system variable from CCU / Homegear"""
          var = None
          if self.remotes[remote]['username'] and self.remotes[remote]['password']:
              LOG.debug(
                  "ServerThread.getSystemVariable: Getting System variable via JSON-RPC")
              session = self.jsonRpcLogin(remote)
              if not session:
                  return
              try:
                  params = {"_session_id_": session, "name": name}
                  response = self._rpcfunctions.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "SysVar.getValueByName", params)
                  if response['error'] is None and response['result']:
                      try:
                          var = float(response['result'])
                      except Exception as err:
                          var = response['result'] == 'true'
  
                  self.jsonRpcLogout(remote, session)
              except Exception as err:
                  self.jsonRpcLogout(remote, session)
                  LOG.warning(
                      "ServerThread.getSystemVariable: Exception: %s" % str(err))
          else:
              try:
                  var = self.proxies[
                      "%s-%s" % (self._interface_id, remote)].getSystemVariable(name)
              except Exception as err:
                  LOG.debug(
                      "ServerThread.getSystemVariable: Exception: %s" % str(err))
          return var
  
      def deleteSystemVariable(self, remote, name):
          """Delete a system variable from CCU / Homegear"""
          if self.remotes[remote]['username'] and self.remotes[remote]['password']:
              LOG.debug(
                  "ServerThread.deleteSystemVariable: Getting System variable via JSON-RPC")
              session = self.jsonRpcLogin(remote)
              if not session:
                  return
              try:
                  params = {"_session_id_": session, "name": name}
                  response = self._rpcfunctions.jsonRpcPost(
                      self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "SysVar.deleteSysVarByName", params)
                  if response['error'] is None and response['result']:
                      deleted = response['result']
                      LOG.warning(
                          "ServerThread.deleteSystemVariable: Deleted: %s" % str(deleted))
  
                  self.jsonRpcLogout(remote, session)
              except Exception as err:
                  self.jsonRpcLogout(remote, session)
                  LOG.warning(
                      "ServerThread.deleteSystemVariable: Exception: %s" % str(err))
          else:
              try:
                  return self.proxies["%s-%s" % (self._interface_id, remote)].deleteSystemVariable(name)
              except Exception as err:
                  LOG.debug(
                      "ServerThread.deleteSystemVariable: Exception: %s" % str(err))
  
      def setSystemVariable(self, remote, name, value):
          """Set a system variable on CCU / Homegear"""
          if self.remotes[remote]['username'] and self.remotes[remote]['password']:
              LOG.debug(
                  "ServerThread.setSystemVariable: Setting System variable via JSON-RPC")
              session = self.jsonRpcLogin(remote)
              if not session:
                  return
              try:
                  params = {"_session_id_": session,
                            "name": name, "value": value}
                  if value is True or value is False:
                      params['value'] = int(value)
                      response = self._rpcfunctions.jsonRpcPost(
                          self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "SysVar.setBool", params)
                  else:
                      response = self._rpcfunctions.jsonRpcPost(
                          self.remotes[remote]['ip'], self.remotes[remote].get('jsonport', DEFAULT_JSONPORT), "SysVar.setFloat", params)
                  if response['error'] is None and response['result']:
                      res = response['result']
                      LOG.debug(
                          "ServerThread.setSystemVariable: Result while setting variable: %s" % str(res))
                  else:
                      if response['error']:
                          LOG.debug("ServerThread.setSystemVariable: Error while setting variable: %s" % str(
                              response['error']))
  
                  self.jsonRpcLogout(remote, session)
              except Exception as err:
                  self.jsonRpcLogout(remote, session)
                  LOG.warning(
                      "ServerThread.setSystemVariable: Exception: %s" % str(err))
          else:
              try:
                  return self.proxies["%s-%s" % (self._interface_id, remote)].setSystemVariable(name, value)
              except Exception as err:
                  LOG.debug(
                      "ServerThread.setSystemVariable: Exception: %s" % str(err))
  
      def getServiceMessages(self, remote):
          """Get service messages from CCU / Homegear"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].getServiceMessages()
          except Exception as err:
              LOG.debug("ServerThread.getServiceMessages: Exception: %s" % str(err))
  
      def rssiInfo(self, remote):
          """Get RSSI information for all devices from CCU / Homegear"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].rssiInfo()
          except Exception as err:
              LOG.debug("ServerThread.rssiInfo: Exception: %s" % str(err))
  
      def setInstallMode(self, remote, on=True, t=60, mode=1, address=None):
          """Activate or deactivate installmode on CCU / Homegear"""
          try:
              args = [on]
              if on and t:
                  args.append(t)
                  if address:
                      args.append(address)
                  else:
                      args.append(mode)
  
              return self.proxies["%s-%s" % (self._interface_id, remote)].setInstallMode(*args)
          except Exception as err:
              LOG.debug("ServerThread.setInstallMode: Exception: %s" % str(err))
  
      def getInstallMode(self, remote):
          """Get remaining time in seconds install mode is active from CCU / Homegear"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].getInstallMode()
          except Exception as err:
              LOG.debug("ServerThread.getInstallMode: Exception: %s" % str(err))
  
      def getAllMetadata(self, remote, address):
          """Get all metadata of device"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].getAllMetadata(address)
          except Exception as err:
              LOG.debug("ServerThread.getAllMetadata: Exception: %s" % str(err))
  
      def getMetadata(self, remote, address, key):
          """Get metadata of device"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].getMetadata(address, key)
          except Exception as err:
              LOG.debug("ServerThread.getMetadata: Exception: %s" % str(err))
  
      def setMetadata(self, remote, address, key, value):
          """Set metadata of device"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].setMetadata(address, key, value)
          except Exception as err:
              LOG.debug("ServerThread.setMetadata: Exception: %s" % str(err))
  
      def deleteMetadata(self, remote, address, key):
          """Delete metadata of device"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].deleteMetadata(address, key)
          except Exception as err:
              LOG.debug("ServerThread.deleteMetadata: Exception: %s" % str(err))
  
      def listBidcosInterfaces(self, remote):
          """Return all available BidCos Interfaces"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].listBidcosInterfaces()
          except Exception as err:
              LOG.debug(
                  "ServerThread.listBidcosInterfaces: Exception: %s" % str(err))
  
      def ping(self, remote):
          """Send ping to CCU/Homegear to generate PONG event"""
          try:
              self.proxies["%s-%s" % (self._interface_id, remote)].ping("%s-%s" % (self._interface_id, remote))
          except Exception as err:
              LOG.warning("ServerThread.ping: Exception: %s" % str(err))
  
      def homegearCheckInit(self, remote):
          """Check if proxy is still initialized"""
          rdict = self.remotes.get(remote)
          if not rdict:
              return False
          if rdict.get('type') != BACKEND_HOMEGEAR:
              return False
          try:
              interface_id = "%s-%s" % (self._interface_id, remote)
              return self.proxies[interface_id].clientServerInitialized(interface_id)
          except Exception as err:
              LOG.debug(
                  "ServerThread.homegearCheckInit: Exception: %s" % str(err))
              return False
  
      def putParamset(self, remote, address, paramset, value):
          """Set paramsets manually"""
          try:
              return self.proxies["%s-%s" % (self._interface_id, remote)].putParamset(address, paramset, value)
          except Exception as err:
              LOG.debug("ServerThread.putParamset: Exception: %s" % str(err))
  