generated from pavel.muhortov/template-bash
	add additional headers to Connect.http()
This commit is contained in:
		
							parent
							
								
									db76bb8e0b
								
							
						
					
					
						commit
						699492f77f
					
				| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
#!/usr/bin/env python3
 | 
			
		||||
# pylint: disable=C0103,C0302,C0114,W0621
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import json
 | 
			
		||||
import logging
 | 
			
		||||
import urllib.request
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +20,7 @@ from paramiko import SSHClient, AutoAddPolicy
 | 
			
		|||
class Parse:
 | 
			
		||||
    """Parser of configs, arguments, parameters.
 | 
			
		||||
    """
 | 
			
		||||
    # pylint: disable=C0123
 | 
			
		||||
    def __init__(self, parameters, block: str = None) -> None:
 | 
			
		||||
        """Object constructor.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +68,7 @@ class Parse:
 | 
			
		|||
        """
 | 
			
		||||
        self.data.update(dictionary)
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=C0206
 | 
			
		||||
    def expand(self, store: str = None) -> dict:
 | 
			
		||||
        """Expand dictionary "key":"name.conf" to dictionary "key":{subkey: subval}.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +83,7 @@ class Parse:
 | 
			
		|||
                config = store + sep + self.data[key]
 | 
			
		||||
            else:
 | 
			
		||||
                config = self.data[key]
 | 
			
		||||
            with open(config) as file:
 | 
			
		||||
            with open(config, encoding='UTF-8') as file:
 | 
			
		||||
                self.data[key] = Parse(file.read()).data
 | 
			
		||||
        return self.data
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +110,7 @@ class Parse:
 | 
			
		|||
        Returns:
 | 
			
		||||
            str: string as "var1=val1;\nvar2=val2;".
 | 
			
		||||
        """
 | 
			
		||||
        with open(config) as file:
 | 
			
		||||
        with open(config, encoding='UTF-8') as file:
 | 
			
		||||
            raw = file.read()
 | 
			
		||||
            strs = ''
 | 
			
		||||
            for line in raw.splitlines():
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +134,9 @@ class Parse:
 | 
			
		|||
            strings = cls.block(blockname, strings)
 | 
			
		||||
        for line in strings.replace('\n', ';').split(';'):
 | 
			
		||||
            if not line.lstrip().startswith('#') and "=" in line:
 | 
			
		||||
                dictionary[line.split('=')[0].strip()] = line.split('=')[1].strip().split(';')[0].strip()
 | 
			
		||||
                dictionary[line.split('=')[0].strip()] = (
 | 
			
		||||
                    line.split('=')[1].strip().split(';')[0].strip()
 | 
			
		||||
                    )
 | 
			
		||||
        return dictionary
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
| 
						 | 
				
			
			@ -171,66 +177,93 @@ class Parse:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class Connect:
 | 
			
		||||
    # pylint: disable=W0105
 | 
			
		||||
    """Set of connection methods (functions) for various protocols.
 | 
			
		||||
    """
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    # pylint: disable=W0102, W0718
 | 
			
		||||
    def http(
 | 
			
		||||
        url: str, method: str = 'GET',
 | 
			
		||||
        username: str = '', password: str = '', authtype: str = None,
 | 
			
		||||
        contenttype: str = 'text/plain', contentdata: str = ''
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        url: str,
 | 
			
		||||
        method: str = 'GET',
 | 
			
		||||
        username: str = '',
 | 
			
		||||
        password: str = '',
 | 
			
		||||
        authtype: (str, type(None)) = None,
 | 
			
		||||
        contenttype: str = 'text/plain',
 | 
			
		||||
        contentdata: (str, bytes) = '',
 | 
			
		||||
        headers: dict = {}
 | 
			
		||||
        ) -> dict:
 | 
			
		||||
        """Handling HTTP request.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            url (str): request url.
 | 
			
		||||
            url (str): Handling HTTP request.
 | 
			
		||||
            method (str, optional): HTTP request method. Defaults to 'GET'.
 | 
			
		||||
            username (str, optional): username for url authentication. Defaults to ''.
 | 
			
		||||
            password (str, optional): password for url authentication. Defaults to ''.
 | 
			
		||||
            authtype (str, optional): digest|basic authentication type. Defaults to None.
 | 
			
		||||
            authtype (str, None, optional): digest|basic authentication type. Defaults to None.
 | 
			
		||||
            contenttype (str, optional): 'Content-Type' header. Defaults to 'text/plain'.
 | 
			
		||||
            contentdata (str, optional): content data. Defaults to ''.
 | 
			
		||||
            contentdata (str, bytes, optional): content data. Defaults to ''.
 | 
			
		||||
            headers (dict, optional): additional headers. Defaults to {}.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: HTTP response or 'ERROR'.
 | 
			
		||||
            dict: {'success':bool,'result':HTTP response or 'ERROR'}.
 | 
			
		||||
        """
 | 
			
		||||
        if Do.args_valid(locals(), Connect.http.__annotations__):
 | 
			
		||||
            if contentdata != '':
 | 
			
		||||
                headers['Content-Type'] = contenttype
 | 
			
		||||
            if isinstance(contentdata, str):
 | 
			
		||||
                contentdata = bytes(contentdata.encode('utf-8'))
 | 
			
		||||
 | 
			
		||||
        # Preparing authorization
 | 
			
		||||
        if authtype:
 | 
			
		||||
            pswd = urllib.request.HTTPPasswordMgrWithDefaultRealm()
 | 
			
		||||
            pswd.add_password(None, url, username, password)
 | 
			
		||||
            if authtype == 'basic':
 | 
			
		||||
                auth = urllib.request.HTTPBasicAuthHandler(pswd)
 | 
			
		||||
            if authtype == 'digest':
 | 
			
		||||
                auth = urllib.request.HTTPDigestAuthHandler(pswd)
 | 
			
		||||
            urllib.request.install_opener(urllib.request.build_opener(auth))
 | 
			
		||||
            # Preparing authorization
 | 
			
		||||
            if authtype:
 | 
			
		||||
                pswd = urllib.request.HTTPPasswordMgrWithDefaultRealm()
 | 
			
		||||
                pswd.add_password(None, url, username, password)
 | 
			
		||||
                if authtype == 'basic':
 | 
			
		||||
                    auth = urllib.request.HTTPBasicAuthHandler(pswd)
 | 
			
		||||
                    token = base64.b64encode((username + ':' + password).encode())
 | 
			
		||||
                    headers['Authorization'] = 'Basic ' + token.decode('utf-8')
 | 
			
		||||
                if authtype == 'digest':
 | 
			
		||||
                    auth = urllib.request.HTTPDigestAuthHandler(pswd)
 | 
			
		||||
                urllib.request.install_opener(urllib.request.build_opener(auth))
 | 
			
		||||
 | 
			
		||||
        # Preparing request
 | 
			
		||||
        request = urllib.request.Request(url=url, data=bytes(contentdata.encode('utf-8')), method=method)
 | 
			
		||||
        request.add_header('Content-Type', contenttype)
 | 
			
		||||
 | 
			
		||||
        # Response
 | 
			
		||||
        try:
 | 
			
		||||
            response = urllib.request.urlopen(request).read()
 | 
			
		||||
            logging.debug(
 | 
			
		||||
                msg=''
 | 
			
		||||
                + '\n' + 'uri: ' + url
 | 
			
		||||
                + '\n' + 'method: ' + method
 | 
			
		||||
                + '\n' + 'username: ' + username
 | 
			
		||||
                + '\n' + 'password: ' + password
 | 
			
		||||
                + '\n' + 'authtype: ' + authtype
 | 
			
		||||
                + '\n' + 'content-type: ' + contenttype
 | 
			
		||||
                + '\n' + 'content-data: ' + contentdata
 | 
			
		||||
            # Preparing request
 | 
			
		||||
            request = urllib.request.Request(
 | 
			
		||||
                url=url,
 | 
			
		||||
                data=contentdata,
 | 
			
		||||
                method=method
 | 
			
		||||
            )
 | 
			
		||||
            if response.startswith(b'\xff\xd8'):
 | 
			
		||||
                return response
 | 
			
		||||
            else:
 | 
			
		||||
                return str(response.decode('utf-8'))
 | 
			
		||||
        except Exception as error:
 | 
			
		||||
            logging.debug(msg='\n' + 'error: ' + str(error))
 | 
			
		||||
            return 'ERROR'
 | 
			
		||||
            for key, val in headers.items():
 | 
			
		||||
                request.add_header(key, val)
 | 
			
		||||
            if len(contentdata) > 128:
 | 
			
		||||
                contentdata = contentdata[:64] + b' ... ' + contentdata[-64:]
 | 
			
		||||
            logging.debug(msg=''
 | 
			
		||||
                    + '\n' + 'uri: ' + url
 | 
			
		||||
                    + '\n' + 'method: ' + method
 | 
			
		||||
                    + '\n' + 'username: ' + username
 | 
			
		||||
                    + '\n' + 'password: ' + password
 | 
			
		||||
                    + '\n' + 'authtype: ' + str(authtype)
 | 
			
		||||
                    + '\n' + 'headers: ' + json.dumps(headers, indent=2)
 | 
			
		||||
                    + '\n' + 'content-data: ' + str(contentdata)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            # Response
 | 
			
		||||
            try:
 | 
			
		||||
                response = urllib.request.urlopen(request).read()
 | 
			
		||||
                if not response.startswith(b'\xff\xd8'):
 | 
			
		||||
                    response = str(response.decode('utf-8'))
 | 
			
		||||
                return {"success": True, "result": response}
 | 
			
		||||
            except Exception as error:
 | 
			
		||||
                logging.debug(msg='\n' + 'error: ' + str(error))
 | 
			
		||||
                return {"success": False, "result": "ERROR"}
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def ssh_commands(command: str, hostname: str, username: str, password: str, port: int = 22) -> str:
 | 
			
		||||
    # pylint: disable=W0718
 | 
			
		||||
    def ssh_commands(
 | 
			
		||||
        command: str,
 | 
			
		||||
        hostname: str,
 | 
			
		||||
        username: str,
 | 
			
		||||
        password: str,
 | 
			
		||||
        port: int = 22
 | 
			
		||||
        ) -> str:
 | 
			
		||||
        """Handling SSH command executing.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +300,15 @@ class Connect:
 | 
			
		|||
            return 'ERROR'
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def ssh_put_file(src_file: str, dst_file: str, hostname: str, username: str, password: str, port: int = 22) -> str:
 | 
			
		||||
    # pylint: disable=W0718
 | 
			
		||||
    def ssh_put_file(
 | 
			
		||||
        src_file: str,
 | 
			
		||||
        dst_file: str,
 | 
			
		||||
        hostname: str,
 | 
			
		||||
        username: str,
 | 
			
		||||
        password: str,
 | 
			
		||||
        port: int = 22
 | 
			
		||||
        ) -> str:
 | 
			
		||||
        """Handling SFTP upload file.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +387,14 @@ class Connect:
 | 
			
		|||
            return 'ERROR'
 | 
			
		||||
    '''
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def ftp_put_file(src_file: str, dst_file: str, hostname: str, username: str, password: str) -> bool:
 | 
			
		||||
    # pylint: disable=W0718,C0116
 | 
			
		||||
    def ftp_put_file(
 | 
			
		||||
        src_file: str,
 | 
			
		||||
        dst_file: str,
 | 
			
		||||
        hostname: str,
 | 
			
		||||
        username: str,
 | 
			
		||||
        password: str
 | 
			
		||||
        ) -> bool:
 | 
			
		||||
        dst_path = dst_file.split('/')[:-1]
 | 
			
		||||
        ftp = FTP(host=hostname)
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -397,7 +445,7 @@ class HikISAPI(Connect):
 | 
			
		|||
        authtype: str = 'digest',
 | 
			
		||||
        hostport: int = 80, protocol: str = 'http',
 | 
			
		||||
        channel: int = 101, videoid: int = 1
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        ) -> None:
 | 
			
		||||
        """Object constructor.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -424,23 +472,28 @@ class HikISAPI(Connect):
 | 
			
		|||
        url: str, method: str = 'GET',
 | 
			
		||||
        contenttype: str = 'application/x-www-form-urlencoded',
 | 
			
		||||
        contentdata: str = ''
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        ) -> str:
 | 
			
		||||
        """Send request to camera.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            url (str): API path for request.
 | 
			
		||||
            method (str, optional): HTTP request method. Defaults to 'GET'.
 | 
			
		||||
            contenttype (str, optional): Content-Type header. Defaults to 'application/x-www-form-urlencoded'.
 | 
			
		||||
            contenttype (str, optional): Content-Type header.
 | 
			
		||||
                Defaults to 'application/x-www-form-urlencoded'.
 | 
			
		||||
            contentdata (str, optional): data for send with request. Defaults to ''.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: HTTP response content.
 | 
			
		||||
            str: HTTP response content or 'ERROR'.
 | 
			
		||||
        """
 | 
			
		||||
        return self.http(
 | 
			
		||||
        response =  self.http(
 | 
			
		||||
            url=url, method=method,
 | 
			
		||||
            username=self._user, password=self._pswd, authtype=self._auth,
 | 
			
		||||
            contenttype=contenttype, contentdata=contentdata
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        if response['success']:
 | 
			
		||||
            return response['result']
 | 
			
		||||
        else:
 | 
			
		||||
            return 'ERROR'
 | 
			
		||||
 | 
			
		||||
    def capabilities(self) -> bool:
 | 
			
		||||
        """Get camera capabilities.
 | 
			
		||||
| 
						 | 
				
			
			@ -451,7 +504,7 @@ class HikISAPI(Connect):
 | 
			
		|||
        url = (
 | 
			
		||||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._viid) + "/capabilities"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.info(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -459,11 +512,16 @@ class HikISAPI(Connect):
 | 
			
		|||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def downloadjpeg(self, dst_file: str = path.splitext(__file__)[0] + '.jpeg', x: int = 1920, y: int = 1080) -> bool:
 | 
			
		||||
    def downloadjpeg(
 | 
			
		||||
        self,
 | 
			
		||||
        dst_file: str = path.splitext(__file__)[0] + '.jpeg',
 | 
			
		||||
        x: int = 1920,
 | 
			
		||||
        y: int = 1080
 | 
			
		||||
        ) -> bool:
 | 
			
		||||
        """Get static picture from camera.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            dst_file (str, optional): absolute path of picture to save. Defaults to scriptname+'.jpeg'.
 | 
			
		||||
            dst_file (str, optional): abs picture's path to save. Defaults to scriptname+'.jpeg'.
 | 
			
		||||
            x (int, optional): picture width. Defaults to 1920.
 | 
			
		||||
            y (int, optional): picture height. Defaults to 1080.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -475,7 +533,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/Streaming/channels/" + str(self._viid)
 | 
			
		||||
            + "/picture?snapShotImageType=JPEG&videoResolutionWidth="
 | 
			
		||||
            + str(x) + "&videoResolutionHeight=" + str(y)
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        with open(dst_file, "wb") as file:
 | 
			
		||||
            response = self.__call(url=url, method='GET')
 | 
			
		||||
            if response != 'ERROR':
 | 
			
		||||
| 
						 | 
				
			
			@ -491,8 +549,10 @@ class HikISAPI(Connect):
 | 
			
		|||
        Returns:
 | 
			
		||||
            bool: True if successed. Printing a response with a logger at the INFO level.
 | 
			
		||||
        """
 | 
			
		||||
        url = (self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
               + "/ISAPI/PTZCtrl/channels/" + str(self._chan) + "/status")
 | 
			
		||||
        url = (
 | 
			
		||||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan) + "/status"
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.info(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -509,7 +569,7 @@ class HikISAPI(Connect):
 | 
			
		|||
        url = (
 | 
			
		||||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/System/reboot"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT")
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -531,7 +591,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=TILT_UP&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -553,7 +613,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=TILT_DOWN&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -575,7 +635,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=PAN_LEFT&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -597,7 +657,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=PAN_RIGHT&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -619,7 +679,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=ZOOM_OUT&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -641,7 +701,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=ZOOM_IN&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -665,7 +725,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + "/PTZControl?command=GOTO_PRESET&presetNo=" + str(preset)
 | 
			
		||||
            + "&speed=" + str(speed)
 | 
			
		||||
            + "&mode=start"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -683,7 +743,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/PTZ/channels/" + str(self._viid)
 | 
			
		||||
            + "/PTZControl?command=GOTO_PRESET&mode=stop"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method='GET')
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -706,7 +766,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan)
 | 
			
		||||
            + "/absolute"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<PTZData><AbsoluteHigh>'
 | 
			
		||||
| 
						 | 
				
			
			@ -726,9 +786,12 @@ class HikISAPI(Connect):
 | 
			
		|||
        """Set camera moving to direction until other signal or 180 seconds elapse.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool: True if successed.
 | 
			
		||||
| 
						 | 
				
			
			@ -737,7 +800,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan)
 | 
			
		||||
            + "/continuous"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<PTZData>'
 | 
			
		||||
| 
						 | 
				
			
			@ -745,7 +808,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + '<tilt>' + str(y) + '</tilt>'
 | 
			
		||||
            + '<zoom>' + str(z) + '</zoom>'
 | 
			
		||||
            + '</PTZData>'
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT", contenttype="text/xml", contentdata=xml)
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -757,10 +820,14 @@ class HikISAPI(Connect):
 | 
			
		|||
        """Set camera moving to direction until other signal or duration elapse.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            t (int, optional): duration in ms of acceleration from 0 to 180000. Defaults to 180000.
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            t (int, optional): duration in ms of acceleration from 0 to 180000.
 | 
			
		||||
                Defaults to 180000.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool: True if successed.
 | 
			
		||||
| 
						 | 
				
			
			@ -769,7 +836,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan)
 | 
			
		||||
            + "/momentary"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<PTZData>'
 | 
			
		||||
| 
						 | 
				
			
			@ -778,7 +845,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + '<zoom>' + str(z) + '</zoom>'
 | 
			
		||||
            + '<Momentary><duration>' + str(t) + '</duration></Momentary>'
 | 
			
		||||
            + '</PTZData>'
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT", contenttype="text/xml", contentdata=xml)
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            sleep(t/1000)
 | 
			
		||||
| 
						 | 
				
			
			@ -791,10 +858,14 @@ class HikISAPI(Connect):
 | 
			
		|||
        """Set camera moving to direction (polymorph abstraction).
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100. Defaults to 0.
 | 
			
		||||
            t (int, optional): duration in ms of acceleration from 0 to 180000. Defaults to 0.
 | 
			
		||||
            x (int, optional): acceleration of horizontal camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            y (int, optional): acceleration of vertical camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            z (int, optional): acceleration of zoom camera movement from -100 to 100.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
            t (int, optional): duration in ms of acceleration from 0 to 180000.
 | 
			
		||||
                Defaults to 0.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool: True if successed.
 | 
			
		||||
| 
						 | 
				
			
			@ -814,11 +885,11 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan)
 | 
			
		||||
            + "/homeposition/goto"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<PTZData></PTZData>'
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT", contenttype="text/xml", contentdata=xml)
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -836,11 +907,11 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/PTZCtrl/channels/" + str(self._chan)
 | 
			
		||||
            + "/homeposition"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<PTZData></PTZData>'
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT", contenttype="text/xml", contentdata=xml)
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -848,7 +919,13 @@ class HikISAPI(Connect):
 | 
			
		|||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def settextonosd(self, enabled: str = "true", x: int = 0, y: int = 0, message: str = "") -> bool:
 | 
			
		||||
    def settextonosd(
 | 
			
		||||
        self,
 | 
			
		||||
        enabled: str = "true",
 | 
			
		||||
        x: int = 0,
 | 
			
		||||
        y: int = 0,
 | 
			
		||||
        message: str = ""
 | 
			
		||||
        ) -> bool:
 | 
			
		||||
        """Set message as video overlay text.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -866,7 +943,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            self._prot + '://' + self._host + ':' + str(self._port)
 | 
			
		||||
            + "/ISAPI/System/Video/inputs/channels/" + str(self._chan)
 | 
			
		||||
            + "/overlays/text"
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        xml = ''.join(
 | 
			
		||||
            '<?xml version="1.0" encoding="UTF-8"?>'
 | 
			
		||||
            + '<TextOverlayList version="1.0" xmlns="http://www.hikvision.com/ver10/XMLSchema">'
 | 
			
		||||
| 
						 | 
				
			
			@ -878,7 +955,7 @@ class HikISAPI(Connect):
 | 
			
		|||
            + '<displayText>' + message + '</displayText>'
 | 
			
		||||
            + '</TextOverlay>'
 | 
			
		||||
            + '</TextOverlayList>'
 | 
			
		||||
        )
 | 
			
		||||
            )
 | 
			
		||||
        response = self.__call(url=url, method="PUT", contenttype="text/xml", contentdata=xml)
 | 
			
		||||
        if response != 'ERROR':
 | 
			
		||||
            logging.debug(msg='\n' + response + '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -896,7 +973,7 @@ class Sensor(Connect):
 | 
			
		|||
        hostname: str, username: str, userpass: str,
 | 
			
		||||
        nodetype: str, nodename: str,
 | 
			
		||||
        hostport: int = 22
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        ) -> None:
 | 
			
		||||
        """Object constructor.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -929,6 +1006,7 @@ class Sensor(Connect):
 | 
			
		|||
            username=self._user, password=self._pswd
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=W0718
 | 
			
		||||
    def __temperature(self, nodename: str) -> str:
 | 
			
		||||
        """Preparating request for ds18b20 sensor type.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -969,21 +1047,25 @@ class Sequence:
 | 
			
		|||
    """Sequence handling.
 | 
			
		||||
    """
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    # pylint: disable=W0718
 | 
			
		||||
    def run(
 | 
			
		||||
        device: HikISAPI, sensors: dict, sequence: dict,
 | 
			
		||||
        records_root_path: str = None,
 | 
			
		||||
        records_root_user: str = None,
 | 
			
		||||
        records_root_pass: str = None
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        ) -> None:
 | 
			
		||||
        """Sequences executor.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            device (HikISAPI): HikISAPI object.
 | 
			
		||||
            sensors (dict): collection as key=sensorname:value=Sensor object.
 | 
			
		||||
            sequence (dict): sequence steps collection.
 | 
			
		||||
            records_root_path (str, optional): path (local|smb|ftp,sftp) to records directory. Defaults to None.
 | 
			
		||||
            records_root_user (str, optional): username if path on remote host. Defaults to None.
 | 
			
		||||
            records_root_pass (str, optional): password if path on remote host. Defaults to None.
 | 
			
		||||
            records_root_path (str, optional): path (local|smb|ftp,sftp) to records directory.
 | 
			
		||||
                Defaults to None.
 | 
			
		||||
            records_root_user (str, optional): username if path on remote host.
 | 
			
		||||
                Defaults to None.
 | 
			
		||||
            records_root_pass (str, optional): password if path on remote host.
 | 
			
		||||
                Defaults to None.
 | 
			
		||||
        """
 | 
			
		||||
        for key, value in sequence.items():
 | 
			
		||||
            action = value.split(',')[0].strip()
 | 
			
		||||
| 
						 | 
				
			
			@ -1002,12 +1084,12 @@ class Sequence:
 | 
			
		|||
                    m = sensor_value
 | 
			
		||||
                else:
 | 
			
		||||
                    m = ''
 | 
			
		||||
            logging.info(
 | 
			
		||||
                msg='   action:' + key + ' = ' + action
 | 
			
		||||
            logging.info(msg=''
 | 
			
		||||
                + '   action:' + key + ' = ' + action
 | 
			
		||||
                + ',' + x + ',' + y + ',' + z
 | 
			
		||||
                + ',' + p + ',' + s + ',' + t
 | 
			
		||||
                + ',' + w + ',' + m
 | 
			
		||||
            )
 | 
			
		||||
                )
 | 
			
		||||
            if action == 'capabilities':
 | 
			
		||||
                response = device.capabilities()
 | 
			
		||||
            elif action == 'getcamerapos':
 | 
			
		||||
| 
						 | 
				
			
			@ -1052,8 +1134,14 @@ class Sequence:
 | 
			
		|||
                th = datetime.now().strftime('%H')
 | 
			
		||||
                tm = datetime.now().strftime('%M')
 | 
			
		||||
                ts = datetime.now().strftime('%S')
 | 
			
		||||
                records_file_name = (key + '_' + dy + '-' + dm + '-' + dd + '_' + th + '.' + tm + '.' + ts + '.jpeg')
 | 
			
		||||
                if device.downloadjpeg(x=int(x), y=int(y), dst_file=records_root_temp + sep + records_file_name):
 | 
			
		||||
                records_file_name = (
 | 
			
		||||
                    key + '_' + dy + '-' + dm + '-' + dd + '_' + th + '.' + tm + '.' + ts + '.jpeg'
 | 
			
		||||
                    )
 | 
			
		||||
                if device.downloadjpeg(
 | 
			
		||||
                    x=int(x),
 | 
			
		||||
                    y=int(y),
 | 
			
		||||
                    dst_file=records_root_temp + sep + records_file_name
 | 
			
		||||
                    ):
 | 
			
		||||
                    hostname = 'localhost'
 | 
			
		||||
                    hostport, hosttype = None, None
 | 
			
		||||
                    username = records_root_user
 | 
			
		||||
| 
						 | 
				
			
			@ -1078,7 +1166,11 @@ class Sequence:
 | 
			
		|||
                            hostname = hostname.split(':')[0]
 | 
			
		||||
                    if hosttype == 'ftp':
 | 
			
		||||
                        src_file = records_root_temp + sep + records_file_name
 | 
			
		||||
                        dst_file = hostpath + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/' + records_file_name
 | 
			
		||||
                        dst_file = (
 | 
			
		||||
                            hostpath
 | 
			
		||||
                            + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/'
 | 
			
		||||
                            + records_file_name
 | 
			
		||||
                            )
 | 
			
		||||
                        if Connect.ftp_put_file(
 | 
			
		||||
                            src_file=src_file,
 | 
			
		||||
                            dst_file=dst_file,
 | 
			
		||||
| 
						 | 
				
			
			@ -1092,7 +1184,11 @@ class Sequence:
 | 
			
		|||
                                pass
 | 
			
		||||
                    elif hosttype == 'sftp':
 | 
			
		||||
                        src_file = records_root_temp + sep + records_file_name
 | 
			
		||||
                        dst_file = hostpath + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/' + records_file_name
 | 
			
		||||
                        dst_file = (
 | 
			
		||||
                            hostpath
 | 
			
		||||
                            + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/'
 | 
			
		||||
                            + records_file_name
 | 
			
		||||
                            )
 | 
			
		||||
                        response = Connect.ssh_put_file(
 | 
			
		||||
                            src_file=src_file, dst_file=dst_file,
 | 
			
		||||
                            hostname=hostname, port=hostport,
 | 
			
		||||
| 
						 | 
				
			
			@ -1107,18 +1203,23 @@ class Sequence:
 | 
			
		|||
                            response = False
 | 
			
		||||
                    else:
 | 
			
		||||
                        src_file = records_root_temp + sep + records_file_name
 | 
			
		||||
                        dst_file = hostpath + sep + dy + sep + dm + sep + dv + sep + dd + sep + records_file_name
 | 
			
		||||
                        dst_file = (
 | 
			
		||||
                            hostpath
 | 
			
		||||
                            + sep + dy + sep + dm + sep + dv + sep + dd + sep
 | 
			
		||||
                            + records_file_name)
 | 
			
		||||
                        try:
 | 
			
		||||
                            makedirs(hostpath + sep + dy + sep + dm + sep + dv + sep + dd, exist_ok=True)
 | 
			
		||||
                            makedirs(
 | 
			
		||||
                                hostpath + sep + dy + sep + dm + sep + dv + sep + dd,
 | 
			
		||||
                                exist_ok=True
 | 
			
		||||
                                )
 | 
			
		||||
                            replace(src=src_file, dst=dst_file)
 | 
			
		||||
                            response = True
 | 
			
		||||
                        except Exception as error:
 | 
			
		||||
                            logging.debug(
 | 
			
		||||
                                msg=''
 | 
			
		||||
                            logging.debug(msg=''
 | 
			
		||||
                                + '\n' + 'src_file: ' + src_file
 | 
			
		||||
                                + '\n' + 'dst_file: ' + dst_file
 | 
			
		||||
                                + '\n' + 'error: ' + str(error)
 | 
			
		||||
                            )
 | 
			
		||||
                                )
 | 
			
		||||
                            response = False
 | 
			
		||||
                else:
 | 
			
		||||
                    response = False
 | 
			
		||||
| 
						 | 
				
			
			@ -1134,6 +1235,7 @@ class Proc:
 | 
			
		|||
    """Find a running process from Python.
 | 
			
		||||
    """
 | 
			
		||||
    @classmethod
 | 
			
		||||
    # pylint: disable=W0612
 | 
			
		||||
    def _list_windows(cls) -> list:
 | 
			
		||||
        """Find all running process with wmi.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1174,6 +1276,7 @@ class Proc:
 | 
			
		|||
        return execlist
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    # pylint: disable=W0612
 | 
			
		||||
    def _list_linux(cls) -> list:
 | 
			
		||||
        """Find all running process with ps.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1219,6 +1322,7 @@ class Proc:
 | 
			
		|||
            return None
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    # pylint: disable=W0150
 | 
			
		||||
    def search(cls, find: str, exclude: str = None) -> list:
 | 
			
		||||
        """Find specified processes.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1301,12 +1405,12 @@ class FFmpeg:
 | 
			
		|||
            int: ffmpeg return code
 | 
			
		||||
        """
 | 
			
		||||
        if not raw:
 | 
			
		||||
            process = (''
 | 
			
		||||
            process = ([]
 | 
			
		||||
                + cls._bin(ffpath).split()
 | 
			
		||||
                + cls._src(src).split()
 | 
			
		||||
                + cls._preset(preset, fps).split()
 | 
			
		||||
                + cls._dst(dst).split()
 | 
			
		||||
            )
 | 
			
		||||
                )
 | 
			
		||||
        else:
 | 
			
		||||
            process = cls._bin(ffpath).split() + raw.split()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1328,7 +1432,7 @@ class FFmpeg:
 | 
			
		|||
                        print(line, flush=True)
 | 
			
		||||
                    else:
 | 
			
		||||
                        que.put(line)
 | 
			
		||||
        return proc.returncode
 | 
			
		||||
                return proc.returncode
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _bin(cls, ffpath: str, tool: str = 'ffmpeg') -> str:
 | 
			
		||||
| 
						 | 
				
			
			@ -1350,11 +1454,11 @@ class FFmpeg:
 | 
			
		|||
            '\tTarget: /usr/bin/ffmpeg\n'
 | 
			
		||||
            '\n'
 | 
			
		||||
            'Install on Windows:\n'
 | 
			
		||||
            '\tDownload and extract archive from: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z\n'
 | 
			
		||||
            '\tDownload and extract: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z\n'
 | 
			
		||||
            '\tTarget: "%PROGRAMFILES%\\ffmpeg\\bin\\ffmpeg.exe"\n'
 | 
			
		||||
            '\n'
 | 
			
		||||
            'Install on MacOS:\n'
 | 
			
		||||
            '\tDownload and extract archive from: https://evermeet.cx/ffmpeg/\n'
 | 
			
		||||
            '\tDownload and extract: https://evermeet.cx/ffmpeg/\n'
 | 
			
		||||
            '\tTarget: /usr/bin/ffmpeg\n'
 | 
			
		||||
            )
 | 
			
		||||
        if not ffpath:
 | 
			
		||||
| 
						 | 
				
			
			@ -1565,7 +1669,8 @@ if __name__ == "__main__":
 | 
			
		|||
               '- Python 3 (tested version 3.9.5), '
 | 
			
		||||
               '- Python 3 modules: paramiko '
 | 
			
		||||
    )
 | 
			
		||||
    args.add_argument('--config', type=str, default=path.splitext(__file__)[0] + '.conf', required=False,
 | 
			
		||||
    args.add_argument('--config', type=str, default=path.splitext(__file__)[0] + '.conf',
 | 
			
		||||
                      required=False,
 | 
			
		||||
                      help='custom configuration file path')
 | 
			
		||||
    args.add_argument('-b', '--broadcast', action='store_true', required=False,
 | 
			
		||||
                      help='streaming media to destination')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user