コード例 #1
0
 def __init__(self, url: str):
     """Initialize the IIHCSoapClient with a url for the controller"""
     self.url = url
     self.username = ""
     self.password = ""
     #if url.startswith("https://"):
     #    self.connection = IHCSSLConnection(url)
     #else:
     self.connection = IHCConnection(url)
コード例 #2
0
 def __init__(self, url: str):
     """Initialize the IIHCSoapClient with a url for the controller"""
     self.url = url
     self.username = ""
     self.password = ""
     self.connection = IHCConnection(url)
コード例 #3
0
class IHCSoapClient:
    """Implements a limited set of the soap request for the IHC controller"""

    ihcns = {
        'SOAP-ENV': "http://schemas.xmlsoap.org/soap/envelope/",
        'ns1': 'utcs',
        'ns2': 'utcs.values',
        'ns3': 'utcs.values'
    }

    def __init__(self, url: str):
        """Initialize the IIHCSoapClient with a url for the controller"""
        self.url = url
        self.username = ""
        self.password = ""
        self.connection = IHCConnection(url)

    def authenticate(self, username: str, password: str) -> bool:
        """Do an Authentricate request and save the cookie returned to be used
        on the following requests.
        Return True if the request was successfull
        """
        self.username = username
        self.password = password

        auth_payload = """<authenticate1 xmlns=\"utcs\"
                          xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
                          <password>{password}</password>
                          <username>{username}</username>
                          <application>treeview</application>
                          </authenticate1>"""
        payload = auth_payload.format(password=self.password,
                                      username=self.username)

        xdoc = self.connection.soap_action('/ws/AuthenticationService',
                                           'authenticate', payload)
        if xdoc is not False:
            isok = xdoc.find(
                './SOAP-ENV:Body/ns1:authenticate2/ns1:loginWasSuccessful',
                IHCSoapClient.ihcns)
            return isok.text == 'true'
        return False

    def get_state(self) -> str:
        """Get the controller state"""
        xdoc = self.connection.soap_action('/ws/ControllerService', 'getState',
                                           "")
        if xdoc is not False:
            return xdoc.find('./SOAP-ENV:Body/ns1:getState1/ns1:state',
                             IHCSoapClient.ihcns).text
        return False

    def wait_for_state_change(self, state: str, waitsec) -> str:
        """Wait for controller state change and return state"""
        payload = """<ns1:waitForControllerStateChange1
                     xmlns:ns1=\"utcs\" xsi:type=\"ns1:WSControllerState\">
                     <ns1:state xsi:type=\"xsd:string\">{state}</ns1:state>
                     </ns1:waitForControllerStateChange1>
                     <ns2:waitForControllerStateChange2
                     xmlns:ns2=\"utcs\" xsi:type=\"xsd:int\">
                     {wait}</ns2:waitForControllerStateChange2>
                     """.format(state=state, wait=waitsec)
        xdoc = self.connection.soap_action('/ws/ControllerService',
                                           'waitForControllerStateChange',
                                           payload)
        if xdoc is not False:
            return xdoc.find(
                './SOAP-ENV:Body/ns1:waitForControllerStateChange3/ns1:state',
                IHCSoapClient.ihcns).text
        return False

    def get_project(self) -> str:
        """Get the ihc project"""
        xdoc = self.connection.soap_action('/ws/ControllerService',
                                           'getIHCProject', "")
        if xdoc is not False:
            base64data = xdoc.find(
                './SOAP-ENV:Body/ns1:getIHCProject1/ns1:data',
                IHCSoapClient.ihcns).text
            if not base64:
                return False
            compresseddata = base64.b64decode(base64data)
            return zlib.decompress(compresseddata,
                                   16 + zlib.MAX_WBITS).decode('ISO-8859-1')
        return False

    def set_runtime_value_bool(self, resourceid: int, value: bool) -> bool:
        """Set a boolean runtime value"""
        if value:
            boolvalue = "true"
        else:
            boolvalue = "false"

        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSBooleanValue\" xmlns:a=\"utcs.values\">
            <a:value>{value}</a:value></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            """.format(id=resourceid, value=boolvalue)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'setResourceValue', payload)
        if xdoc is not False:
            result = xdoc.find(r'./SOAP-ENV:Body/ns1:setResourceValue2',
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_int(self, resourceid: int, intvalue: int):
        """Set a integer runtime value"""
        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSIntegerValue\" xmlns:a=\"utcs.values\">
            <a:integer>{value}</a:integer></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            """.format(id=resourceid, value=intvalue)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'setResourceValue', payload)
        if xdoc is not False:
            result = xdoc.find('./SOAP-ENV:Body/ns1:setResourceValue2',
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_float(self, resourceid: int, floatvalue: float):
        """Set a flot runtime value"""
        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSFloatingPointValue\" xmlns:a=\"utcs.values\">
            <a:floatingPointValue>{value}</a:floatingPointValue></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            </s:Body>
            """.format(id=resourceid, value=floatvalue)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'setResourceValue', payload)
        if xdoc is not False:
            result = xdoc.find('./SOAP-ENV:Body/ns1:setResourceValue2',
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def get_runtime_value(self, resourceid: int):
        """Get runtime value of specified resource it
           The returned value will be boolean, integer or float
           Return None if resource cannot be found or on error
        """
        payload = """<getRuntimeValue1 xmlns="utcs">{id}</getRuntimeValue1>
                  """.format(id=resourceid)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'getResourceValue', payload)
        if xdoc is False:
            return False
        boolresult = xdoc.find(
            './SOAP-ENV:Body/ns1:getRuntimeValue2/ns1:value/ns2:value',
            IHCSoapClient.ihcns)
        if boolresult is not None:
            return boolresult.text == "true"
        intresult = xdoc.find(
            './SOAP-ENV:Body/ns1:getRuntimeValue2/ns1:value/ns2:integer',
            IHCSoapClient.ihcns)
        if intresult is not None:
            return int(intresult.text)
        floatresult = xdoc.find(('./SOAP-ENV:Body/ns1:getRuntimeValue2/'
                                 'ns1:value/ns2:floatingPointValue'),
                                IHCSoapClient.ihcns)
        if floatresult is not None:
            return float(floatresult.text)
        enumNameResut = xdoc.find(('./SOAP-ENV:Body/ns1:getRuntimeValue2/'
                                   'ns1:value/ns2:enumName'),
                                  IHCSoapClient.ihcns)
        if enumNameResut is not None:
            return enumNameResut.text
        return False

    def enable_runtime_notification(self, resourceid: int):
        """Enable notification for specified resource id"""
        return self.enable_runtime_notifications([resourceid])

    def enable_runtime_notifications(self, resourceids):
        """Enable notification for specified resource ids"""
        idsarr = ""
        for ihcid in resourceids:
            idsarr += "<a:arrayItem>{id}</a:arrayItem>".format(id=ihcid)

        payload = """<enableRuntimeValueNotifications1 xmlns=\"utcs\"
                     xmlns:a=\"http://www.w3.org/2001/XMLSchema\"
                     xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
                     {arr}
                     </enableRuntimeValueNotifications1>
                     """.format(arr=idsarr)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'enableRuntimeValueNotifications',
                                           payload)
        return xdoc is not False

    def wait_for_resource_value_changes(self, wait: int = 10):
        """
        Long polling for changes and return a dictionary with resource:value
        for changes
        """
        changes = {}
        payload = """<waitForResourceValueChanges1
                     xmlns=\"utcs\">{timeout}</waitForResourceValueChanges1>
                  """.format(timeout=wait)
        xdoc = self.connection.soap_action('/ws/ResourceInteractionService',
                                           'getResourceValue', payload)
        if xdoc is False:
            return False
        result = xdoc.findall(
            './SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem',
            IHCSoapClient.ihcns)
        for item in result:
            ihcid = item.find('ns1:resourceID', IHCSoapClient.ihcns)
            if ihcid is None:
                continue
            bvalue = item.find('./ns1:value/ns2:value', IHCSoapClient.ihcns)
            if bvalue is not None:
                changes[int(ihcid.text)] = bvalue.text == 'true'
                continue
            ivalue = item.find('./ns1:value/ns3:integer', IHCSoapClient.ihcns)
            if ivalue is not None:
                changes[int(ihcid.text)] = int(ivalue.text)
            fvalue = item.find('./ns1:value/ns2:floatingPointValue',
                               IHCSoapClient.ihcns)
            if fvalue is not None:
                changes[int(ihcid.text)] = float(fvalue.text)
                continue

            enumName = item.find('./ns1:value/ns2:enumName',
                                 IHCSoapClient.ihcns)
            if enumName is not None:
                changes[int(ihcid.text)] = enumName.text
        return changes

    def get_user_log(self, language='da'):
        """Get the controller state"""
        payload = """<getUserLog1 xmlns="utcs" />
                     <getUserLog2 xmlns="utcs">0</getUserLog2>
                     <getUserLog3 xmlns="utcs">{language}</getUserLog3>
                     """.format(language=language)
        xdoc = self.connection.soap_action('/ws/ConfigurationService',
                                           'getUserLog', payload)
        if xdoc is not False:
            base64data = xdoc.find('./SOAP-ENV:Body/ns1:getUserLog4/ns1:data',
                                   IHCSoapClient.ihcns).text
            if not base64data:
                return False
            return base64.b64decode(base64data).decode('UTF-8')
        return False
コード例 #4
0
class IHCSoapClient:
    """Implements a limited set of the soap request for the IHC controller"""

    ihcns = {
        "SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
        "ns1": "utcs",
        "ns2": "utcs.values",
        "ns3": "utcs.values",
    }

    def __init__(self, url: str):
        """Initialize the IIHCSoapClient with a url for the controller"""
        self.url = url
        self.username = ""
        self.password = ""
        #if url.startswith("https://"):
        #    self.connection = IHCSSLConnection(url)
        #else:
        self.connection = IHCConnection(url)

    def authenticate(self, username: str, password: str) -> bool:
        """Do an Authentricate request and save the cookie returned to be used
        on the following requests.
        Return True if the request was successfull
        """
        self.username = username
        self.password = password

        auth_payload = """<authenticate1 xmlns=\"utcs\"
                          xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
                          <password>{password}</password>
                          <username>{username}</username>
                          <application>treeview</application>
                          </authenticate1>"""
        payload = auth_payload.format(password=self.password,
                                      username=self.username)

        xdoc = self.connection.soap_action("/ws/AuthenticationService",
                                           "authenticate", payload)
        if xdoc is not False:
            isok = xdoc.find(
                "./SOAP-ENV:Body/ns1:authenticate2/ns1:loginWasSuccessful",
                IHCSoapClient.ihcns,
            )
            return isok.text == "true"
        return False

    def get_state(self) -> str:
        """Get the controller state"""
        xdoc = self.connection.soap_action("/ws/ControllerService", "getState",
                                           "")
        if xdoc is not False:
            return xdoc.find("./SOAP-ENV:Body/ns1:getState1/ns1:state",
                             IHCSoapClient.ihcns).text
        return False

    def wait_for_state_change(self, state: str, waitsec) -> str:
        """Wait for controller state change and return state"""
        payload = """<ns1:waitForControllerStateChange1
                     xmlns:ns1=\"utcs\" xsi:type=\"ns1:WSControllerState\">
                     <ns1:state xsi:type=\"xsd:string\">{state}</ns1:state>
                     </ns1:waitForControllerStateChange1>
                     <ns2:waitForControllerStateChange2
                     xmlns:ns2=\"utcs\" xsi:type=\"xsd:int\">
                     {wait}</ns2:waitForControllerStateChange2>
                     """.format(state=state, wait=waitsec)
        xdoc = self.connection.soap_action("/ws/ControllerService",
                                           "waitForControllerStateChange",
                                           payload)
        if xdoc is not False:
            return xdoc.find(
                "./SOAP-ENV:Body/ns1:waitForControllerStateChange3/ns1:state",
                IHCSoapClient.ihcns,
            ).text
        return False

    def get_project(self) -> str:
        """Get the ihc project"""
        xdoc = self.connection.soap_action("/ws/ControllerService",
                                           "getIHCProject", "")
        if xdoc is not False:
            base64data = xdoc.find(
                "./SOAP-ENV:Body/ns1:getIHCProject1/ns1:data",
                IHCSoapClient.ihcns).text
            if not base64:
                return False
            compresseddata = base64.b64decode(base64data)
            return zlib.decompress(compresseddata,
                                   16 + zlib.MAX_WBITS).decode("ISO-8859-1")
        return False

    def set_runtime_value_bool(self, resourceid: int, value: bool) -> bool:
        """Set a boolean runtime value"""
        if value:
            boolvalue = "true"
        else:
            boolvalue = "false"

        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSBooleanValue\" xmlns:a=\"utcs.values\">
            <a:value>{value}</a:value></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            """.format(id=resourceid, value=boolvalue)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "setResourceValue", payload)
        if xdoc is not False:
            result = xdoc.find(r"./SOAP-ENV:Body/ns1:setResourceValue2",
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_int(self, resourceid: int, intvalue: int):
        """Set a integer runtime value"""
        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSIntegerValue\" xmlns:a=\"utcs.values\">
            <a:integer>{value}</a:integer></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            """.format(id=resourceid, value=intvalue)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "setResourceValue", payload)
        if xdoc is not False:
            result = xdoc.find("./SOAP-ENV:Body/ns1:setResourceValue2",
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_float(self, resourceid: int, floatvalue: float):
        """Set a flot runtime value"""
        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSFloatingPointValue\" xmlns:a=\"utcs.values\">
            <a:floatingPointValue>{value}</a:floatingPointValue></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            </s:Body>
            """.format(id=resourceid, value=floatvalue)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "setResourceValue", payload)
        if xdoc is not False:
            result = xdoc.find("./SOAP-ENV:Body/ns1:setResourceValue2",
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_timer(self, resourceid: int, timer: int):
        """Set a timer runtime value in milliseconds"""
        payload = """
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSTimerValue\" xmlns:a=\"utcs.values\">
            <a:milliseconds>{value}</a:milliseconds></value>
            <typeString/>
            <resourceID>{id}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            </s:Body>
            """.format(id=resourceid, value=timer)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "setResourceValue", payload)
        if xdoc is not False:
            result = xdoc.find("./SOAP-ENV:Body/ns1:setResourceValue2",
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def set_runtime_value_time(self, resourceid: int, hours: int, minutes: int,
                               seconds: int):
        """Set a time runtime value in hours:minutes:seconds"""
        payload = f"""
            <setResourceValue1 xmlns=\"utcs\"
            xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
            <value i:type=\"a:WSTimeValue\" xmlns:a=\"utcs.values\">
            <a:hours>{hours}</a:hours>
            <a:minutes>{minutes}</a:minutes>
            <a:seconds>{seconds}</a:seconds>
            </value>
            <typeString/>
            <resourceID>{resourceid}</resourceID>
            <isValueRuntime>true</isValueRuntime>
            </setResourceValue1>
            </s:Body>
            """
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "setResourceValue", payload)
        if xdoc is not False:
            result = xdoc.find("./SOAP-ENV:Body/ns1:setResourceValue2",
                               IHCSoapClient.ihcns).text
            return result == "true"
        return False

    def get_time(resource_value):

        hours = int(
            resource_value.find("./ns2:hours", IHCSoapClient.ihcns).text)
        minutes = int(
            resource_value.find("./ns2:minutes", IHCSoapClient.ihcns).text)
        seconds = int(
            resource_value.find("./ns2:seconds", IHCSoapClient.ihcns).text)

        return datetime.time(hours, minutes, seconds)

    def __get_value(resource_value):
        """Get a runtime value from the xml base on the type in the xml"""
        valuetype = resource_value.attrib[
            "{http://www.w3.org/2001/XMLSchema-instance}type"].split(":")[1]
        result = {
            "WSBooleanValue":
            lambda v:
            (v.find("./ns2:value", IHCSoapClient.ihcns).text == "true"),
            "WSIntegerValue":
            lambda v: int(v.find("./ns2:integer", IHCSoapClient.ihcns).text),
            "WSFloatingPointValue":
            lambda v: float(
                v.find("./ns2:floatingPointValue", IHCSoapClient.ihcns).text),
            "WSEnumValue":
            lambda v: v.find("./ns2:enumName", IHCSoapClient.ihcns).text,
            "WSTimerValue":
            lambda v: int(
                v.find("./ns2:milliseconds", IHCSoapClient.ihcns).text),
            "WSTimeValue":
            lambda v: IHCSoapClient.get_time(v),
        }[valuetype](resource_value)

        return result

    def get_runtime_value(self, resourceid: int):
        """Get runtime value of specified resource it
        The returned value will be boolean, integer or float
        Return None if resource cannot be found or on error
        """
        payload = """<getRuntimeValue1 xmlns="utcs">{id}</getRuntimeValue1>
                  """.format(id=resourceid)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "getResourceValue", payload)
        if xdoc is False:
            return None
        value = xdoc.find("./SOAP-ENV:Body/ns1:getRuntimeValue2/ns1:value",
                          IHCSoapClient.ihcns)
        return IHCSoapClient.__get_value(value)

    def get_runtime_values(self, resourceids):
        """Get runtime values of specified resource ids
        Return None if resource cannot be found or on error
        """

        idsarr = ""
        for ihcid in resourceids:
            idsarr += "<arrayItem>{id}</arrayItem>".format(id=ihcid)
        payload = '<getRuntimeValues1 xmlns="utcs">' + idsarr + "</getRuntimeValues1>"
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "getResourceValues", payload)
        if xdoc is False:
            return False
        result = xdoc.findall(
            "./SOAP-ENV:Body/ns1:getRuntimeValues2/ns1:arrayItem",
            IHCSoapClient.ihcns)
        changes = {}
        for item in result:
            ihcid = item.find("ns1:resourceID", IHCSoapClient.ihcns)
            if ihcid is None:
                continue
            resourceValue = item.find("./ns1:value", IHCSoapClient.ihcns)
            itemValue = IHCSoapClient.__get_value(resourceValue)
            if itemValue is not None:
                changes[int(ihcid.text)] = itemValue
        return changes

    def cycle_bool_value(self, resourceid: int):
        """Turn a booelan resource On and back Off
        Return None if resource cannot be found or on error
        """
        setBool = ("<arrayItem>"
                   '<value xsi:type="ns1:WSBooleanValue">'
                   "<ns1:value>{value}</ns1:value>"
                   "</value>"
                   "<typeString></typeString>"
                   "<resourceID>{id}</resourceID>"
                   "<isValueRuntime>true</isValueRuntime>"
                   "</arrayItem>")
        payload = (
            '<setResourceValues1 xmlns="utcs" xmlns:ns1="utcs.values">' +
            setBool.format(value="true", id=resourceid) +
            setBool.format(value="false", id=resourceid) +
            "</setResourceValues1>")
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "SOAPAction: setResourceValues",
                                           payload)
        if xdoc is False:
            return None
        return True

    def enable_runtime_notification(self, resourceid: int):
        """Enable notification for specified resource id"""
        return self.enable_runtime_notifications([resourceid])

    def enable_runtime_notifications(self, resourceids):
        """Enable notification for specified resource ids"""
        idsarr = ""
        for ihcid in resourceids:
            idsarr += "<a:arrayItem>{id}</a:arrayItem>".format(id=ihcid)

        payload = """<enableRuntimeValueNotifications1 xmlns=\"utcs\"
                     xmlns:a=\"http://www.w3.org/2001/XMLSchema\"
                     xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
                     {arr}
                     </enableRuntimeValueNotifications1>
                     """.format(arr=idsarr)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "enableRuntimeValueNotifications",
                                           payload)
        return xdoc is not False

    def wait_for_resource_value_changes(self, wait: int = 10):
        """
        Long polling for changes and return a dictionary with resource:value
        for changes
        """
        changes = {}
        payload = """<waitForResourceValueChanges1
                     xmlns=\"utcs\">{timeout}</waitForResourceValueChanges1>
                  """.format(timeout=wait)
        xdoc = self.connection.soap_action("/ws/ResourceInteractionService",
                                           "getResourceValue", payload)
        if xdoc is False:
            return False
        result = xdoc.findall(
            "./SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem",
            IHCSoapClient.ihcns,
        )
        for item in result:
            ihcid = item.find("ns1:resourceID", IHCSoapClient.ihcns)
            if ihcid is None:
                continue

            resource_value = item.find("./ns1:value", IHCSoapClient.ihcns)
            value = IHCSoapClient.__get_value(resource_value)
            if value is not None:
                changes[int(ihcid.text)] = value
        return changes

    def get_user_log(self, language="da"):
        """Get the controller state"""
        payload = """<getUserLog1 xmlns="utcs" />
                     <getUserLog2 xmlns="utcs">0</getUserLog2>
                     <getUserLog3 xmlns="utcs">{language}</getUserLog3>
                     """.format(language=language)
        xdoc = self.connection.soap_action("/ws/ConfigurationService",
                                           "getUserLog", payload)
        if xdoc is not False:
            base64data = xdoc.find("./SOAP-ENV:Body/ns1:getUserLog4/ns1:data",
                                   IHCSoapClient.ihcns).text
            if not base64data:
                return False
            return base64.b64decode(base64data).decode("UTF-8")
        return False

    def clear_user_log(self):
        """Clear the user log in the controller"""
        xdoc = self.connection.soap_action("/ws/ConfigurationService",
                                           "clearUserLog", "")
        return

    def get_system_info(self):
        """Get controller system info"""
        xdoc = self.connection.soap_action("/ws/ConfigurationService",
                                           "getSystemInfo", "")
        if xdoc is False:
            return False
        info = {
            "uptime":
            IHCSoapClient.__extract_sysinfo(xdoc, "uptime"),
            "realtimeclock":
            IHCSoapClient.__extract_sysinfo(xdoc, "realtimeclock"),
            "serial_number":
            IHCSoapClient.__extract_sysinfo(xdoc, "serialNumber"),
            "production_date":
            IHCSoapClient.__extract_sysinfo(xdoc, "productionDate"),
            "brand":
            IHCSoapClient.__extract_sysinfo(xdoc, "brand"),
            "version":
            IHCSoapClient.__extract_sysinfo(xdoc, "version"),
            "hw_revision":
            IHCSoapClient.__extract_sysinfo(xdoc, "hwRevision"),
            "sw_date":
            IHCSoapClient.__extract_sysinfo(xdoc, "swDate"),
            "dataline_version":
            IHCSoapClient.__extract_sysinfo(xdoc, "datalineVersion"),
            "rf_module_software_version":
            IHCSoapClient.__extract_sysinfo(xdoc, "rfModuleSoftwareVersion"),
            "rf_module_serial_number":
            IHCSoapClient.__extract_sysinfo(xdoc, "rfModuleSerialNumber"),
            "application_is_without_viewer":
            IHCSoapClient.__extract_sysinfo(xdoc,
                                            "applicationIsWithoutViewer"),
            "sms_modem_software_version":
            IHCSoapClient.__extract_sysinfo(xdoc, "smsModemSoftwareVersion"),
            "led_dimmer_software_version":
            IHCSoapClient.__extract_sysinfo(xdoc, "ledDimmerSoftwareVersion"),
        }
        return info

    def __extract_sysinfo(xdoc, param) -> str:
        """Internal function to extrach a parameter from system info"""
        element = xdoc.find(f"./SOAP-ENV:Body/ns1:getSystemInfo1/ns1:{param}",
                            IHCSoapClient.ihcns)
        if element is None:
            return None
        return element.text