From ab5d6bf686595068f096ce03896989ea92fbbfaa Mon Sep 17 00:00:00 2001 From: Victor Alexandrovich Tsyrenschikov <77172321+tsyrenschikov@users.noreply.github.com> Date: Wed, 16 Jul 2025 01:02:10 +0500 Subject: [PATCH] EspHome --- synology_api/__init__.py | 25 + synology_api/audiostation.py | 79 + synology_api/auth.py | 408 +++ synology_api/base_api.py | 34 + synology_api/core_active_backup.py | 185 + synology_api/core_backup.py | 314 ++ synology_api/core_certificate.py | 137 + synology_api/core_sys_info.py | 1006 +++++ synology_api/dhcp_server.py | 72 + synology_api/directory_server.py | 798 ++++ synology_api/docker_api.py | 55 + synology_api/downloadstation.py | 344 ++ synology_api/drive_admin_console.py | 146 + synology_api/error_codes.py | 228 ++ synology_api/exceptions.py | 320 ++ synology_api/filestation.py | 1243 +++++++ synology_api/log_center.py | 70 + synology_api/notestation.py | 100 + synology_api/oauth.py | 31 + synology_api/photos.py | 287 ++ synology_api/qnap.py | 7 + synology_api/security_advisor.py | 54 + synology_api/snapshot.py | 261 ++ synology_api/surveillancestation.py | 5267 +++++++++++++++++++++++++++ synology_api/universal_search.py | 33 + synology_api/usb_copy.py | 47 + synology_api/virtualization.py | 334 ++ synology_api/vpn.py | 218 ++ 28 files changed, 12103 insertions(+) create mode 100644 synology_api/__init__.py create mode 100644 synology_api/audiostation.py create mode 100644 synology_api/auth.py create mode 100644 synology_api/base_api.py create mode 100644 synology_api/core_active_backup.py create mode 100644 synology_api/core_backup.py create mode 100644 synology_api/core_certificate.py create mode 100644 synology_api/core_sys_info.py create mode 100644 synology_api/dhcp_server.py create mode 100644 synology_api/directory_server.py create mode 100644 synology_api/docker_api.py create mode 100644 synology_api/downloadstation.py create mode 100644 synology_api/drive_admin_console.py create mode 100644 synology_api/error_codes.py create mode 100644 synology_api/exceptions.py create mode 100644 synology_api/filestation.py create mode 100644 synology_api/log_center.py create mode 100644 synology_api/notestation.py create mode 100644 synology_api/oauth.py create mode 100644 synology_api/photos.py create mode 100644 synology_api/qnap.py create mode 100644 synology_api/security_advisor.py create mode 100644 synology_api/snapshot.py create mode 100644 synology_api/surveillancestation.py create mode 100644 synology_api/universal_search.py create mode 100644 synology_api/usb_copy.py create mode 100644 synology_api/virtualization.py create mode 100644 synology_api/vpn.py diff --git a/synology_api/__init__.py b/synology_api/__init__.py new file mode 100644 index 0000000..3ec9cfa --- /dev/null +++ b/synology_api/__init__.py @@ -0,0 +1,25 @@ +from . import \ + audiostation, \ + auth, \ + base_api, \ + directory_server, \ + docker_api, \ + drive_admin_console, \ + core_active_backup, \ + core_backup, \ + core_certificate, \ + core_sys_info, \ + downloadstation, \ + log_center, \ + vpn, \ + oauth, \ + security_advisor, \ + dhcp_server, \ + notestation, \ + filestation, \ + photos, \ + usb_copy, \ + virtualization, \ + universal_search, \ + snapshot, \ + surveillancestation diff --git a/synology_api/audiostation.py b/synology_api/audiostation.py new file mode 100644 index 0000000..fd9d881 --- /dev/null +++ b/synology_api/audiostation.py @@ -0,0 +1,79 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class AudioStation(base_api.BaseApi): + + def get_info(self) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.Info' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo'} + return self.request_data(api_name, api_path, req_param) + + def get_playlist_info(self) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.Playlist' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'list', 'library': 'all', 'limit': '100000', 'version': info['maxVersion']} + + return self.request_data(api_name, api_path, req_param) + + def list_remote_player(self) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'list', 'type': 'all', 'additional': 'subplayer_list', 'version': info['maxVersion']} + + return self.request_data(api_name, api_path, req_param) + + def list_pinned_song(self) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.Pin' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'list', 'version': info['maxVersion']} + + return self.request_data(api_name, api_path, req_param) + + def device_id(self, device: str) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'getplaylist', 'id': device, 'version': info['maxVersion']} + + return self.request_data(api_name, api_path, req_param) + + # You Must choose the device if any from list_remote_player() + + def remote_play(self, device: str) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'control', 'id': device, 'version': info['maxVersion'], 'action': 'play'} + + return self.request_data(api_name, api_path, req_param) + + def remote_stop(self, device: str) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'control', 'id': device, 'version': info['maxVersion'], 'action': 'stop'} + + return self.request_data(api_name, api_path, req_param) + + def remote_next(self, device: str) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'control', 'id': device, 'version': info['maxVersion'], 'action': 'next'} + + return self.request_data(api_name, api_path, req_param) + + def remote_prev(self, device: str) -> dict[str, object] | str: + api_name = 'SYNO.AudioStation.RemotePlayer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'method': 'control', 'id': device, 'version': info['maxVersion'], 'action': 'prev'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/auth.py b/synology_api/auth.py new file mode 100644 index 0000000..a579e09 --- /dev/null +++ b/synology_api/auth.py @@ -0,0 +1,408 @@ +from __future__ import annotations +from typing import Optional +import requests +import json +from .error_codes import error_codes, CODE_SUCCESS, download_station_error_codes, file_station_error_codes +from .error_codes import auth_error_codes, virtualization_error_codes +from urllib3 import disable_warnings +from urllib3.exceptions import InsecureRequestWarning +from .exceptions import SynoConnectionError, HTTPError, JSONDecodeError, LoginError, LogoutError, DownloadStationError +from .exceptions import FileStationError, AudioStationError, ActiveBackupError, VirtualizationError, BackupError +from .exceptions import CertificateError, DHCPServerError, DirectoryServerError, DockerError, DriveAdminError +from .exceptions import LogCenterError, NoteStationError, OAUTHError, PhotosError, SecurityAdvisorError +from .exceptions import UniversalSearchError, USBCopyError, VPNError, CoreSysInfoError, UndefinedError + +USE_EXCEPTIONS: bool = True + + +class Authentication: + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None + ) -> None: + + self._ip_address: str = ip_address + self._port: str = port + self._username: str = username + self._password: str = password + self._sid: Optional[str] = None + self._syno_token: Optional[str] = None + self._session_expire: bool = True + self._verify: bool = cert_verify + self._version: int = dsm_version + self._debug: bool = debug + self._otp_code: Optional[str] = otp_code + + if self._verify is False: + disable_warnings(InsecureRequestWarning) + + schema = 'https' if secure else 'http' + + self._base_url = '%s://%s:%s/webapi/' % (schema, self._ip_address, self._port) + + self.full_api_list = {} + self.app_api_list = {} + + def verify_cert_enabled(self) -> bool: + return self._verify + + def login(self, application: str) -> None: + login_api = 'auth.cgi?api=SYNO.API.Auth' + params = {'version': self._version, 'method': 'login', 'account': self._username, + 'passwd': self._password, 'session': application, 'format': 'cookie', 'enable_syno_token':'yes'} + if self._otp_code: + params['otp_code'] = self._otp_code + + if not self._session_expire and self._sid is not None: + self._session_expire = False + if self._debug is True: + print('User already logged in') + else: + # Check request for error: + session_request_json: dict[str, object] = {} + if USE_EXCEPTIONS: + try: + session_request = requests.get(self._base_url + login_api, params, verify=self._verify) + session_request.raise_for_status() + session_request_json = session_request.json() + except requests.exceptions.ConnectionError as e: + raise SynoConnectionError(error_message=e.args[0]) + except requests.exceptions.HTTPError as e: + raise HTTPError(error_message=str(e.args)) + except requests.exceptions.JSONDecodeError as e: + raise JSONDecodeError(error_message=str(e.args)) + else: + # Will raise its own errors: + session_request = requests.get(self._base_url + login_api, params, verify=self._verify) + session_request_json = session_request.json() + + # Check dsm response for error: + error_code = self._get_error_code(session_request_json) + if not error_code: + self._sid = session_request_json['data']['sid'] + self._syno_token = session_request_json['data']['synotoken'] + self._session_expire = False + if self._debug is True: + print('User logged in, new session started!') + else: + self._sid = None + if self._debug is True: + print('Login failed: ' + self._get_error_message(error_code, 'Auth')) + if USE_EXCEPTIONS: + raise LoginError(error_code=error_code) + return + + def logout(self, application: str) -> None: + logout_api = 'auth.cgi?api=SYNO.API.Auth' + param = {'version': self._version, 'method': 'logout', 'session': application} + + if USE_EXCEPTIONS: + try: + response = requests.get(self._base_url + logout_api, param, verify=self._verify) + response.raise_for_status() + response_json = response.json() + error_code = self._get_error_code(response_json) + except requests.exceptions.ConnectionError as e: + raise SynoConnectionError(error_message=e.args[0]) + except requests.exceptions.HTTPError as e: + raise HTTPError(error_message=str(e.args)) + except requests.exceptions.JSONDecodeError as e: + raise JSONDecodeError(error_message=str(e.args)) + else: + response = requests.get(self._base_url + logout_api, param, verify=self._verify) + error_code = self._get_error_code(response.json()) + self._session_expire = True + self._sid = None + if self._debug is True: + if not error_code: + print('Successfully logged out.') + else: + print('Logout failed: ' + self._get_error_message(error_code, 'Auth')) + if USE_EXCEPTIONS and error_code: + raise LogoutError(error_code=error_code) + + return + + def get_api_list(self, app: Optional[str] = None) -> None: + query_path = 'query.cgi?api=SYNO.API.Info' + list_query = {'version': '1', 'method': 'query', 'query': 'all'} + + if USE_EXCEPTIONS: + # Check request for error, and raise our own error.: + try: + response = requests.get(self._base_url + query_path, list_query, verify=self._verify) + response.raise_for_status() + response_json = response.json() + except requests.exceptions.ConnectionError as e: + raise SynoConnectionError(error_message=e.args[0]) + except requests.exceptions.HTTPError as e: + raise HTTPError(error_message=str(e.args)) + except requests.JSONDecodeError as e: + raise JSONDecodeError(error_message=str(e.args)) + else: + # Will raise its own errors: + response_json = requests.get(self._base_url + query_path, list_query, verify=self._verify).json() + + if app is not None: + for key in response_json['data']: + if app.lower() in key.lower(): + self.app_api_list[key] = response_json['data'][key] + else: + self.full_api_list = response_json['data'] + + return + + def show_api_name_list(self) -> None: + prev_key = '' + for key in self.full_api_list: + if key != prev_key: + print(key) + prev_key = key + return + + def show_json_response_type(self) -> None: + for key in self.full_api_list: + for sub_key in self.full_api_list[key]: + if sub_key == 'requestFormat': + if self.full_api_list[key]['requestFormat'] == 'JSON': + print(key + ' Returns JSON data') + return + + def search_by_app(self, app: str) -> None: + print_check = 0 + for key in self.full_api_list: + if app.lower() in key.lower(): + print(key) + print_check += 1 + continue + if print_check == 0: + print('Not Found') + return + + def request_multi_datas(self, + compound: dict[object] = None, + method: Optional[str] = None, + mode: Optional[str] = "sequential", # "sequential" or "parallel" + response_json: bool = True + ) -> dict[str, object] | str | list | requests.Response: # 'post' or 'get' + + ''' + Compound is a json structure that contains multiples requests, you can execute them sequential or parallel + + Example of compound: + compound = [ + { + "api": "SYNO.Core.User", + "method": "list", + "version": self.core_list["SYNO.Core.User"] + } + ] + ''' + api_path = self.full_api_list['SYNO.Entry.Request']['path'] + api_version = self.full_api_list['SYNO.Entry.Request']['maxVersion'] + url = f"{self._base_url}{api_path}" + + req_param = { + "api": "SYNO.Entry.Request", + "method": "request", + "version": f"{api_version}", + "mode": mode, + "stop_when_error": "true", + "_sid": self._sid, + "compound": json.dumps(compound) + } + + if method is None: + method = 'get' + + ## Request need some headers to work properly + # X-SYNO-TOKEN is the token that we get when we login + # We get it from the self._syno_token variable and by param 'enable_syno_token':'yes' in the login request + + if method == 'get': + response = requests.get(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + elif method == 'post': + response = requests.post(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + + + + + + if response_json is True: + return response.json() + else: + return response + + + + def request_data(self, + api_name: str, + api_path: str, + req_param: dict[str, object], + method: Optional[str] = None, + response_json: bool = True + ) -> dict[str, object] | str | list | requests.Response: # 'post' or 'get' + + # Convert all boolean in string in lowercase because Synology API is waiting for "true" or "false" + for k, v in req_param.items(): + if isinstance(v, bool): + req_param[k] = str(v).lower() + + if method is None: + method = 'get' + + req_param['_sid'] = self._sid + + url = ('%s%s' % (self._base_url, api_path)) + '?api=' + api_name + + # Do request and check for error: + response: Optional[requests.Response] = None + if USE_EXCEPTIONS: + # Catch and raise our own errors: + try: + if method == 'get': + response = requests.get(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + elif method == 'post': + response = requests.post(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + except requests.exceptions.ConnectionError as e: + raise SynoConnectionError(error_message=e.args[0]) + except requests.exceptions.HTTPError as e: + raise HTTPError(error_message=str(e.args)) + else: + # Will raise its own error: + if method == 'get': + response = requests.get(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + elif method == 'post': + response = requests.post(url, req_param, verify=self._verify, headers={"X-SYNO-TOKEN":self._syno_token}) + + # Check for error response from dsm: + error_code = 0 + if USE_EXCEPTIONS: + # Catch a JSON Decode error: + try: + error_code = self._get_error_code(response.json()) + except requests.exceptions.JSONDecodeError: + pass + else: + # Will raise its own error: + error_code = self._get_error_code(response.json()) + + if error_code: + if self._debug is True: + print('Data request failed: ' + self._get_error_message(error_code, api_name)) + + if USE_EXCEPTIONS: + # Download station error: + if api_name.find('DownloadStation') > -1: + raise DownloadStationError(error_code=error_code) + # File station error: + elif api_name.find('FileStation') > -1: + raise FileStationError(error_code=error_code) + # Audio station error: + elif api_name.find('AudioStation') > -1: + raise AudioStationError(error_code=error_code) + # Active backup error: + elif api_name.find('ActiveBackup') > -1: + raise ActiveBackupError(error_code=error_code) + # Virtualization error: + elif api_name.find('Virtualization') > -1: + raise VirtualizationError(error_code=error_code) + # Syno backup error: + elif api_name.find('SYNO.Backup') > -1: + raise BackupError(error_code=error_code) + # Core certificate error: + elif api_name.find('Core.Certificate') > -1: + raise CertificateError(error_code=error_code) + # DHCP Server error: + elif api_name.find('DHCPServer') > -1 or api_name == 'SYNO.Core.TFTP': + raise DHCPServerError(error_code=error_code) + # Active Directory error: + elif api_name.find('ActiveDirectory') > -1 or api_name in ('SYNO.Auth.ForgotPwd', 'SYNO.Entry.Request'): + raise DirectoryServerError(error_code=error_code) + # Docker Error: + elif api_name.find('Docker') > -1: + raise DockerError(error_code=error_code) + # Synology drive admin error: + elif api_name.find('SynologyDrive') > -1 or api_name == 'SYNO.C2FS.Share': + raise DriveAdminError(error_code=error_code) + # Log center error: + elif api_name.find('LogCenter') > -1: + raise LogCenterError(error_code=error_code) + # Note station error: + elif api_name.find('NoteStation') > -1: + raise NoteStationError(error_code=error_code) + # OAUTH error: + elif api_name.find('SYNO.OAUTH') > -1: + raise OAUTHError(error_code=error_code) + # Photo station error: + elif api_name.find('SYNO.Foto') > -1: + raise PhotosError(error_code=error_code) + # Security advisor error: + elif api_name.find('SecurityAdvisor') > -1: + raise SecurityAdvisorError(error_code=error_code) + # Universal search error: + elif api_name.find('SYNO.Finder') > -1: + raise UniversalSearchError(error_code=error_code) + # USB Copy error: + elif api_name.find('SYNO.USBCopy') > -1: + raise USBCopyError(error_code=error_code) + # VPN Server error: + elif api_name.find('VPNServer') > -1: + raise VPNError(error_code=error_code) + # Core Sys Info: + elif api_name.find('SYNO.Core') > -1: + raise CoreSysInfoError(error_code=error_code) + elif api_name.find('SYNO.Storage') > -1: + raise CoreSysInfoError(error_code=error_code) + elif api_name.find('SYNO.ResourceMonitor') > -1: + raise CoreSysInfoError(error_code=error_code) + elif (api_name in ('SYNO.Backup.Service.NetworkBackup', 'SYNO.Finder.FileIndexing.Status', + 'SYNO.S2S.Server.Pair')): + raise CoreSysInfoError(error_code=error_code) + # Unhandled API: + else: + raise UndefinedError(error_code=error_code, api_name=api_name) + + if response_json is True: + return response.json() + else: + return response + + @staticmethod + def _get_error_code(response: dict[str, object]) -> int: + if response.get('success'): + code = CODE_SUCCESS + else: + code = response.get('error').get('code') + return code + + @staticmethod + def _get_error_message(code: int, api_name: str) -> str: + if code in error_codes.keys(): + message = error_codes[code] + elif api_name == 'Auth': + message = auth_error_codes.get(code, "") + elif api_name.find('DownloadStation') > -1: + message = download_station_error_codes.get(code, "") + elif api_name.find('Virtualization') > -1: + message = virtualization_error_codes.get(code, "") + elif api_name.find('FileStation') > -1: + message = file_station_error_codes.get(code, "") + else: + message = "" % api_name + return 'Error {} - {}'.format(code, message) + + @property + def sid(self) -> Optional[str]: + return self._sid + + @property + def base_url(self) -> str: + return self._base_url diff --git a/synology_api/base_api.py b/synology_api/base_api.py new file mode 100644 index 0000000..9494048 --- /dev/null +++ b/synology_api/base_api.py @@ -0,0 +1,34 @@ +from typing import Optional, Any +from . import auth as syn + + +class BaseApi(object): + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None, + application: str = 'Core', + ) -> None: + + self.application = application + self.session: syn.Authentication = syn.Authentication(ip_address, port, username, password, secure, cert_verify, + dsm_version, debug, otp_code) + self.session.login(self.application) + self.session.get_api_list(self.application) + self.session.get_api_list() + + self.request_data: Any = self.session.request_data + self.core_list: Any = self.session.app_api_list + self.gen_list: Any = self.session.full_api_list + self._sid: str = self.session.sid + self.base_url: str = self.session.base_url + + def logout(self) -> None: + self.session.logout(self.application) + return diff --git a/synology_api/core_active_backup.py b/synology_api/core_active_backup.py new file mode 100644 index 0000000..0663ed1 --- /dev/null +++ b/synology_api/core_active_backup.py @@ -0,0 +1,185 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + +import time +import json + + +class ActiveBackupBusiness(base_api.BaseApi): + + def list_vm_hypervisor(self) -> dict[str, object] | str: + ''' + This function returns a list of list of all configured hypervisors present in ABB. + ''' + api_name = 'SYNO.ActiveBackup.Inventory' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def list_device_transfer_size(self, + time_start: int = int(time.time() - 86400), + time_end: int = int(time.time())) -> dict[str, object] | str: + ''' + This function returns a list of all devices and their respective transfer size for the given time frame. Default value is 24 hours. + ''' + api_name = 'SYNO.ActiveBackup.Overview' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list_device_transfer_size', + 'time_start': time_start, + 'time_end': time_end} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_list(self, load_versions: bool = False, filter: dict[str, any] = {}) -> dict[str, object] | str: + ''' + This function returns a list of all tasks. Can also retrieve versions corresponding to each task with the `load_versions` parameter. + + `filter` can be used to retrieve only specific information: + + filter: dict[str, any] = {} + "task_id": int, + "backup_type": int, + "load_available": bool, + "limit": int, + "is_snapshot": bool, + etc.. + + ''' + api_name = 'SYNO.ActiveBackup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list', + 'load_status': 'true', + 'load_result': 'true', + 'load_versions': load_versions, + 'filter': json.dumps(filter)} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_run(self, task_ids: list[int]) -> dict[str, object] | str: + ''' + This function will trigger a backup event for the given tasks. Even if only one task is specified, a list has to be passed as argument. + ''' + api_name = 'SYNO.ActiveBackup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'backup', + 'task_ids': str(task_ids), + 'trigger_type': '1'} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_cancel(self, task_ids: list[int]) -> dict[str, object] | str: + ''' + This function will trigger a cancel backup event for the given tasks. Even if only one task is specified, a list has to be passed as argument. + ''' + api_name = 'SYNO.ActiveBackup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'cancel', + 'task_ids': str(task_ids)} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_remove(self, task_ids: list[int]) -> dict[str, object] | str: + ''' + This function will trigger a task deletion event for the given tasks. Even if only one task is specified, a list has to be passed as argument. + ''' + api_name = 'SYNO.ActiveBackup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'remove', + 'task_ids': str(task_ids)} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_delete_versions(self, task_id: int, versions_ids: list[int]) -> dict[str, object] | str: + ''' + This function will trigger a version deletion event for the given version. Even if only one version is specified, a list has to be passed as argument. + ''' + api_name = 'SYNO.ActiveBackup.Version' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'delete', + 'task_id': task_id, + 'version_ids': str(versions_ids)} + + return self.request_data(api_name, api_path, req_param) + + def list_logs(self, filter: dict[str, any] = {}) -> dict[str, object] | str: + ''' + This function returns a dictionary of the logs of all tasks. + + `filter` can be used to retrieve only specific information: + + filter: dict[str, any] = {} + "task_id": int, + "backup_type": int, + "load_available": bool, + "limit": int, + "is_snapshot": bool, + etc.. + + ''' + api_name = 'SYNO.ActiveBackup.Log' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list_result', + 'filter': json.dumps(filter)} + + return self.request_data(api_name, api_path, req_param) + + def list_logs_details(self, + result_id: int, + limit: int = 500, + order_by: str = "log_level", + direction: str = "ASC") -> dict[str, object] | str: + ''' + This function returns a dictionary of the logs of a given task event. `result_id` can be retrieved from `list_logs()` function. + ''' + api_name = 'SYNO.ActiveBackup.Log' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list_result_detail', + 'result_id': result_id, + 'limit': limit, + 'order_by': order_by, + 'direction': direction} + + return self.request_data(api_name, api_path, req_param) + + def list_storage(self) -> dict[str, object] | str: + ''' + This function returns a dictionary of the current storages being used by ABB. + ''' + api_name = 'SYNO.ActiveBackup.Share' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'list_storage'} + + return self.request_data(api_name, api_path, req_param) + diff --git a/synology_api/core_backup.py b/synology_api/core_backup.py new file mode 100644 index 0000000..20cfa27 --- /dev/null +++ b/synology_api/core_backup.py @@ -0,0 +1,314 @@ +from __future__ import annotations +from . import base_api + + +class Backup(base_api.BaseApi): + + def backup_repository_get(self, task_id: str) -> dict[str, object] | str: + ''' + Get repository information for given task. + ''' + api_name = 'SYNO.Backup.Repository' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'get', 'task_id': task_id} + + return self.request_data(api_name, api_path, req_param) + + def backup_repository_list(self) -> dict[str, object] | str: + ''' + Get a list of all present repositories in Hyper Backup. + ''' + api_name = 'SYNO.Backup.Repository' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_list(self) -> dict[str, object] | str: + ''' + Get current restoring information and a list of present tasks in Hyper Backup. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_status(self, task_id: str) -> dict[str, object] | str: + ''' + Get status and state of task. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'status', 'task_id': task_id} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_get(self, task_id: str) -> dict[str, object] | str: + ''' + Get detailed task information. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'get', 'task_id': task_id} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_result(self, task_id: str) -> dict[str, object] | str: + ''' + Get last result summary information of a task. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'status', 'blOnline': 'false', + 'additional': '["last_bkp_time","next_bkp_time","last_bkp_result","is_modified","last_bkp_progress"]', + 'task_id': task_id} + + return self.request_data(api_name, api_path, req_param) + + def backup_task_run(self, task_id: str) -> dict[str, object] | str: + ''' + Run backup task for corresponding task_id. + If the task is not in backupable state, the API will return an error, usually 44xx. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'backup', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def backup_task_cancel(self, task_id: str) -> dict[str, object] | str: + ''' + Cancel currently running backup task. + If the task is not running, the API will return an error, usually 44xx. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'cancel', + 'task_state': 'backupable', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def backup_task_suspend(self, task_id: str) -> dict[str, object] | str: + ''' + Suspend currently running backup task. + If the task is not running or not yet suspendable, the API will return an error, usually 44xx. + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'suspend', + 'task_state': 'backupable', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def backup_task_discard(self, task_id: str) -> dict[str, object] | str: + ''' + Discard currently suspended backup task. + If the task is not suspended, the request will not fail, and will fail to discard the task, leaving the task state as "Failed". + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'discard', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def backup_task_resume(self, task_id: str) -> dict[str, object] | str: + ''' + Discard currently suspended backup task. + If the task is not suspended, the request will not fail, and will fail to resume the task, leaving the task state as "Failed". + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'resume', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def backup_task_remove(self, task_id_list: str) -> dict[str, object] | str: + ''' + Remove one or more backup tasks. + Data in destination will not be removed. It is still possible to relink the task using the original .hbk file. + The API requires an array of tasks to remove, it should be passed as a string with the following format: + `task_id_list = '[29]'` || `task_id_list = '[29,15]'` + ''' + api_name = 'SYNO.Backup.Task' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'delete', + 'is_remove_data': False, + 'task_id_list': task_id_list + } + return self.request_data(api_name, api_path, req_param) + + def integrity_check_run(self, task_id: str) -> dict[str, object] | str: + ''' + Run integrity check for backup task. + If the task is running, the request will not fail, and will fail to perform the integrity check due to target is busy. + ''' + api_name = 'SYNO.Backup.Target' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'error_detect', + 'detect_data': True, + 'sessId': 'null', + 'sessKey': 'null', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def integrity_check_cancel(self, task_id: str) -> dict[str, object] | str: + ''' + Cancel currently running integrity check for backup task. + If integrity check is not running, the API will return an error, usually 44xx. + ''' + api_name = 'SYNO.Backup.Target' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'error_detect_cancel', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def hb_logs_get(self, + limit: int = 1000, + offset: int = 0, + filter_keyword: str = '', + # filter_level: str = '', For some reason when passing filter_level, the API returns error 120. + filter_date_from: int = 0, + filter_date_to: int = 0) -> dict[str, object] | str: + ''' + Get Hyper Backup UI logs. + + `filter_date_from` and `filter_date_to` need to be passed in epoch format. + ''' + api_name = 'SYNO.SDS.Backup.Client.Common.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'list', + 'limit': limit, + 'offset': offset, + 'filter_keyword': filter_keyword, + # 'filter_level': filter_level, + 'filter_date_from': filter_date_from, + 'filter_date_to': filter_date_to + } + return self.request_data(api_name, api_path, req_param) + + def vault_target_list(self) -> dict[str, object]: # Should be working now + ''' + List all available targets in Vault. + ''' + api_name = 'SYNO.Backup.Service.VersionBackup.Target' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def vault_concurrency_get(self) -> dict[str, object]: + ''' + Get number of concurrent tasks allowed to run in HB Vault. Default value is 2. + ''' + api_name = 'SYNO.Backup.Service.VersionBackup.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'get' + } + return self.request_data(api_name, api_path, req_param) + + def vault_concurrency_set(self, parallel_backup_limit: int = 2) -> dict[str, object]: + ''' + Set number of concurrent tasks allowed to run in HB Vault. Default value is 2. + ''' + api_name = 'SYNO.Backup.Service.VersionBackup.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'set', + 'parallel_backup_limit': parallel_backup_limit + } + return self.request_data(api_name, api_path, req_param) + + def vault_target_settings_get(self, target_id: int) -> dict[str, object]: + ''' + Get settings of target. + ''' + api_name = 'SYNO.Backup.Service.VersionBackup.Target' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'detail', + 'target_id': target_id + } + return self.request_data(api_name, api_path, req_param) + + def vault_task_statistics_get(self, task_id: int) -> dict[str, object]: + ''' + Get statistics for given task. + ''' + api_name = 'SYNO.SDS.Backup.Server.Common.Statistic' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'get', + 'additional': '["volume_size"]', + 'task_id': task_id + } + return self.request_data(api_name, api_path, req_param) + + def vault_target_logs_get(self, + target_id: int, + limit: int = 1000, + offset: int = 0) -> dict[str, object]: + ''' + Get logs for given task. + ''' + api_name = 'SYNO.SDS.Backup.Server.Common.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = { + 'version': info['minVersion'], + 'method': 'list', + 'limit': limit, + 'offset': offset, + 'filter_target_id': target_id + } + return self.request_data(api_name, api_path, req_param) \ No newline at end of file diff --git a/synology_api/core_certificate.py b/synology_api/core_certificate.py new file mode 100644 index 0000000..1a4dc1b --- /dev/null +++ b/synology_api/core_certificate.py @@ -0,0 +1,137 @@ +from __future__ import annotations +from io import BytesIO +from typing import Optional + +from . import base_api + +import os +import requests +import json + + +class Certificate(base_api.BaseApi): + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None + ) -> None: + super(Certificate, self).__init__(ip_address, port, username, password, secure, cert_verify, dsm_version, debug, + otp_code) + self._debug: bool = debug + + def _base_certificate_methods(self, + method: str, + cert_id: Optional[str] = None, + ids: Optional[str | list[str]] = None + ) -> str | dict[str, object]: + available_method = ['list', 'set', 'delete'] + if method not in available_method: + # print error here + return f"Method {method} no supported." + + api_name = 'SYNO.Core.Certificate.CRT' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': method} + + if 'set' == method and cert_id: + req_param.update( + {'as_default': 'true', + 'desc': '\"\"', + 'id': f"\"{cert_id}\"" + }) + elif 'delete' == method and ids: + ids = json.dumps(ids) + req_param.update({"ids": ids}) + + return self.request_data(api_name, api_path, req_param) + + def list_cert(self) -> dict[str, object]: + return self._base_certificate_methods('list') + + def set_default_cert(self, cert_id: str) -> dict[str, object]: + return self._base_certificate_methods('set', cert_id) + + def delete_certificate(self, ids: str | list[str]) -> dict[str, object]: + if isinstance(ids, str): + ids = [ids] + return self._base_certificate_methods('delete', ids=ids) + + def upload_cert(self, + serv_key: str = "server.key", + ser_cert: str = "server.crt", + ca_cert: Optional[str] = None, + set_as_default: bool = True, + cert_id: Optional[str] = None, + desc: Optional[str] = None + ) -> tuple[int, dict[str, object]]: + api_name = 'SYNO.Core.Certificate' + info = self.session.app_api_list[api_name] + api_path = info['path'] + serv_key = os.path.abspath(serv_key) + ser_cert = os.path.abspath(ser_cert) + # ca_cert is optional argument for upload cert + ca_cert = os.path.abspath(ca_cert) if ca_cert else None + + session = requests.session() + + url = ('%s%s' % (self.base_url, api_path)) + '?api=%s&version=%s&method=import&_sid=%s' % ( + api_name, info['minVersion'], self._sid) + + if cert_id: + print("update exist cert: " + cert_id) + data_payload = {'id': cert_id or '', 'desc': desc or '', 'as_default': 'true' if set_as_default else 'false'} + + with open(serv_key, 'rb') as payload_serv_key, open(ser_cert, 'rb') as payload_ser_cert: + files = {'key': (serv_key, payload_serv_key, 'application/x-x509-ca-cert'), + 'cert': (ser_cert, payload_ser_cert, 'application/x-x509-ca-cert')} + if ca_cert: + with open(ca_cert, 'rb') as payload_ca_cert: + files['inter_cert'] = (ca_cert, payload_ca_cert, 'application/x-x509-ca-cert') + r = session.post(url, files=files, data=data_payload, verify=self.session.verify_cert_enabled(), headers={"X-SYNO-TOKEN":self.session._syno_token}) + else: + r = session.post(url, files=files, data=data_payload, verify=self.session.verify_cert_enabled(), headers={"X-SYNO-TOKEN":self.session._syno_token}) + + if 200 == r.status_code and r.json()['success']: + if self._debug is True: + print('Certificate upload successful.') + + return r.status_code, r.json() + + def export_cert(self, cert_id: str) -> Optional[BytesIO]: + """Export a certificate from the Synology NAS. + + :param cert_id: The certificate ID to export. This can be found in the list_cert() method. + :return: A BytesIO object containing the certificate archive. + """ + + api_name = "SYNO.Core.Certificate" + info = self.session.app_api_list[api_name] + api_path = info['path'] + + session = requests.session() + + url = ( + f"{self.base_url}{api_path}?" + f"api={api_name}&" + f"version={info['minVersion']}&" + f"method=export&" + f"file=\"archive\"&" + f"_sid={self._sid}&" + f"id={cert_id}" + ) + + result = session.get(url, verify=self.session.verify_cert_enabled(), headers={"X-SYNO-TOKEN":self.session._syno_token}) + + if result.status_code == 200: + return BytesIO(result.content) + + return diff --git a/synology_api/core_sys_info.py b/synology_api/core_sys_info.py new file mode 100644 index 0000000..5ddf8b1 --- /dev/null +++ b/synology_api/core_sys_info.py @@ -0,0 +1,1006 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class SysInfo(base_api.BaseApi): + + def fileserv_smb(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.SMB' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def fileserv_afp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.AFP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def fileserv_nfs(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.NFS' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def fileserv_ftp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.FTP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def fileserv_sftp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.FTP.SFTP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_backup_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Backup.Service.NetworkBackup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def bandwidth_control_protocol(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.BandwidthControl.Protocol' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'protocol': 'NetworkBackup'} + + return self.request_data(api_name, api_path, req_param) + + def shared_folders_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Share' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def services_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Service' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def services_discovery(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.ServiceDiscovery' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def file_transfer_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.FileTransfer' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def web_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Web.DSM' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def current_connection(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.CurrentConnection' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def bandwidth_control_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.BandwidthControl.Status' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def sys_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System.Status' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def latest_logs(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.Status' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'latestlog_get'} + + return self.request_data(api_name, api_path, req_param) + + def client_notify_settings_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.Setting.Notify' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def get_security_scan_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SecurityScan.Conf' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'first_get'} + + return self.request_data(api_name, api_path, req_param) + + def get_security_scan_rules(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SecurityScan.Status' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'items': 'ALL', 'method': 'rule_get'} + + return self.request_data(api_name, api_path, req_param) + + def get_security_scan_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SecurityScan.Status' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'system_get'} + + return self.request_data(api_name, api_path, req_param) + + def get_user_list(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.User' + info = self.core_list[api_name] + api_path = info['path'] + additional = '["email", "description", "expired"]' + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': additional} + + return self.request_data(api_name, api_path, req_param) + + def quickconnect_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.QuickConnect' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_misc_config'} + + return self.request_data(api_name, api_path, req_param) + + def quickconnect_permissions(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.QuickConnect.Permission' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_topology(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Router.Topology' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_wifi_client(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Wifi.Client' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_bond(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Bond' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_bridge(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Bridge' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_ethernet(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Ethernet' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_local_bridge(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.LocalBridge' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_usb_modem(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.USBModem' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_pppoe(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.PPPoE' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_ipv6tunnel(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.IPv6Tunnel' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_vpn_pptp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.VPN.PPTP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_openvpn(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.VPN.OpenVPN' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': '["status"]'} + + return self.request_data(api_name, api_path, req_param) + + def network_vpn_l2tp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.VPN.L2TP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def domain_schedule(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.Domain.Schedule' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def client_ldap(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.LDAP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def client_sso(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.SSO' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def sys_upgrade_check(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.Server' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'check'} + + return self.request_data(api_name, api_path, req_param) + + def sys_upgrade_download(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.Server.Download' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'progress'} + + return self.request_data(api_name, api_path, req_param) + + def sys_upgrade_setting(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.Setting' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def notification_sms_conf(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Notification.SMS.Conf' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def notification_mail_conf(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Notification.Mail.Conf' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def notification_push_mail(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Notification.Push.Mail' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def notification_push_conf(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Notification.Push.Conf' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def hardware_beep_control(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.BeepControl' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def hardware_fan_speed(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.FanSpeed' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def set_fan_speed(self, fan_speed: str = 'quietfan') -> dict[str, object] | str: # coolfan , fullfan + api_name = 'SYNO.Core.Hardware.FanSpeed' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', "dual_fan_speed": fan_speed} + + return self.request_data(api_name, api_path, req_param) + + def enable_zram(self, enable_zram: bool = True) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.ZRAM' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', "enable_zram": str(enable_zram).lower()} + + return self.request_data(api_name, api_path, req_param) + + def enable_power_recovery(self, + restart_auto_after_issue: bool = True, + wake_on_lan: bool = False + ) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.PowerRecovery' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', + "rc_power_config": str(restart_auto_after_issue).lower(), 'wol1': str(wake_on_lan).lower()} + + return self.request_data(api_name, api_path, req_param) + + def enable_beep_control(self, + fan_fail: Optional[bool] = None, + volume_crash: Optional[bool] = None, + poweron_beep: Optional[bool] = None, + poweroff_beep: Optional[bool] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.BeepControl' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', "fan_fail": str(fan_fail).lower(), + "volume_crash": str(volume_crash).lower(), "poweron_beep": str(poweron_beep).lower(), + "poweroff_beep": str(poweroff_beep).lower()} + + return self.request_data(api_name, api_path, req_param) + + def set_led_control(self, led_brightness: int = 2) -> dict[str, object] | str: + api_name = 'SYNO.Core.Led.Brightness' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', 'led_brightness': led_brightness} + + return self.request_data(api_name, api_path, req_param) + + def set_hibernation(self, internal_hd_idletime: int = 0, usb_idletime: int = 0) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.Hibernation' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', 'internal_hd_idletime': internal_hd_idletime, + 'usb_idletime': usb_idletime} + + return self.request_data(api_name, api_path, req_param) + + def enable_external_ups(self, + enable: bool = False, + mode: str = 'SLAVE', + delay_time: int = 1, + snmp_auth_key_dirty: bool = False, + snmp_privacy_key_dirty: bool = False + ) -> dict[str, object] | str: + api_name = 'SYNO.Core.ExternalDevice.UPS' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set', 'enable': str(enable).lower(), 'mode': mode, + 'delay_time': delay_time, 'snmp_auth_key_dirty': str(snmp_auth_key_dirty).lower(), + 'snmp_privacy_key_dirty': str(snmp_privacy_key_dirty).lower()} + + return self.request_data(api_name, api_path, req_param) + + def get_system_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'info'} + + return self.request_data(api_name, api_path, req_param) + + def get_cpu_temp(self) -> str: + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'info'} + + return self.request_data(api_name, api_path, req_param)['data']['sys_temp'] + + def get_all_system_utilization(self) -> str: + api_name = 'SYNO.Core.System.Utilization' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param)['data'] + + def get_cpu_utilization(self) -> str: + api_name = 'SYNO.Core.System.Utilization' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param)['data']['cpu'] + + def get_disk_utilization(self) -> str: + api_name = 'SYNO.Core.System.Utilization' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param)['data']['disk'] + + def get_memory_utilization(self) -> str: + api_name = 'SYNO.Core.System.Utilization' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param)['data']['memory'] + + def shutdown(self): + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'shutdown'} + + return self.request_data(api_name, api_path, req_param) + + def reboot(self): + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'reboot'} + + return self.request_data(api_name, api_path, req_param) + + def dsm_info(self) -> dict[str, object] | str: + api_name = 'SYNO.DSM.Info' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo'} + + return self.request_data(api_name, api_path, req_param) + def get_network_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'info', 'type': 'network'} + + return self.request_data(api_name, api_path, req_param) + + def get_volume_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'info', 'type': 'storage_v2'} + + return self.request_data(api_name, api_path, req_param) + + def hardware_hibernation(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.Hibernation' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def hardware_ups(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.ExternalDevice.UPS' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def hardware_power_schedule(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Hardware.PowerSchedule' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load'} + + return self.request_data(api_name, api_path, req_param) + + def terminal_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Terminal' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def snmp_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SNMP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def process(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System.Process' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def storage(self) -> dict[str, object] | str: + api_name = 'SYNO.Storage.CGI.Storage' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load_info'} + + return self.request_data(api_name, api_path, req_param) + + def external_device_storage_usb(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.ExternalDevice.Storage.USB' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', + 'additional': ['dev_type', 'product', 'status', 'partitions']} + + return self.request_data(api_name, api_path, req_param) + + def external_device_storage_esata(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.ExternalDevice.Storage.eSATA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': ['dev_type', 'status']} + + return self.request_data(api_name, api_path, req_param) + + def file_index_resource(self) -> dict[str, object] | str: + api_name = 'SYNO.Finder.FileIndexing.Status' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def cms_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.CMS.Info' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + # TODO {'error': {'code': 2502}, 'success': False} + '''def service_port_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Service.PortInfo' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'target': ['port_forward']} + + return self.request_data(api_name, api_path, req_param)''' + + def port_forwarding_rules(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.PortForwarding.Rules' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load'} + + return self.request_data(api_name, api_path, req_param) + + def port_forwarding_router_conf(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.PortForwarding.RouterConf' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def disk_list(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Polling.Data' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def ddns_provider_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.DDNS.Provider' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def ddns_record_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.DDNS.Record' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def ddns_external_ip(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.DDNS.ExtIP' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'retry': 'true'} + + return self.request_data(api_name, api_path, req_param) + + def ddns_synology(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.DDNS.Synology' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_myds_account'} + + return self.request_data(api_name, api_path, req_param) + + def iscsi_lun_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.ISCSI.LUN' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def hddman(self) -> dict[str, object] | str: + api_name = 'SYNO.Storage.CGI.HddMan' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def ftp_security_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.FTP.Security' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def bandwidth_control_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.BandwidthControl.Protocol' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'protocol': 'FTP'} + + return self.request_data(api_name, api_path, req_param) + + def directory_domain_info(self) -> dict[str, object] | str: # TODO to test + api_name = 'SYNO.Core.Directory.Domain' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def ws_transfer_info(self) -> dict[str, object] | str: # TODO to test + api_name = 'SYNO.Core.FileServ.ServiceDiscovery.WSTransfer' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def ref_link_copy_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.ReflinkCopy' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def bonjour_service_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.ExternalDevice.Printer.BonjourSharing' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def users_info(self, offset: int = 0, limit: int = -1) -> dict[str, object] | str: + api_name = 'SYNO.Core.User' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'type': 'local', 'offset': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def password_policy(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.User.PasswordPolicy' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def password_expiry(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.User.PasswordExpiry' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def personal_photo_enable(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.User.Home' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def ftp_chroot_user(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileServ.FTP.ChrootUser' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load'} + + return self.request_data(api_name, api_path, req_param) + + def server_pair(self) -> dict[str, object] | str: + api_name = 'SYNO.S2S.Server.Pair' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': ['sync_shares']} + + return self.request_data(api_name, api_path, req_param) + + def groups_info(self, offset: int = 0, limit: int = -1, name_only: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.Core.Group' + info = self.core_list[api_name] + api_path = info['path'] + + if name_only: + name_only = 'true' + elif not name_only: + name_only = 'false' + else: + return 'name_only must be True or False' + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': offset, 'limit': limit, + 'name_only': name_only, 'type': 'local'} + + return self.request_data(api_name, api_path, req_param) + + def ldap_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.LDAP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + # TODO {'error': {'code': 103}, 'success': False} + '''def domain_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.Domain' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'get': {'additional': 'true'}} + + return self.request_data(api_name, api_path, req_param)''' + + def sso_iwa_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.SSO.IWA' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def sso_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Directory.SSO' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_interface_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Interface' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def proxy_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Proxy' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def gateway_list(self, ip_type: str = 'ipv4', type: str = 'wan') -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Router.Gateway.List' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'iptype': ip_type, 'type': type} + + return self.request_data(api_name, api_path, req_param) + + def firewall_info(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Security.Firewall.Profile' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + # TODO {'error': {'code': 101}, 'success': False} + '''def upgrade_schedule_set(self, week_day=4, hour=4, minute=10) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.Setting' + info = self.core_list[api_name] + api_path = info['path'] + + schedule = {'week_day': '4', 'hour': 4, 'minute': 10} + req_param = {'version': info['maxVersion'], 'method': 'set', 'autoupdate_type': 'hotfix-security', + 'schedule': schedule} + + return self.request_data(api_name, api_path, req_param)''' + + def auto_upgrade_status(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.AutoUpgrade' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + return self.request_data(api_name, api_path, req_param) + + def upgrade_server_check(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Upgrade.Server' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'check', 'user_reading': 'true', + 'need_auto_smallupdate': 'true', 'need_promotion': 'true'} + + return self.request_data(api_name, api_path, req_param) + + def alarm_rules_logs(self) -> dict[str, object] | str: + api_name = 'SYNO.ResourceMonitor.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': 0, 'limit': 100, + 'sort_direction': 'DESC', 'sort_by': 'time', 'mode': 'sequential'} + + return self.request_data(api_name, api_path, req_param) + + def alarm_rules_list(self) -> dict[str, object] | str: + api_name = 'SYNO.ResourceMonitor.EventRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def resource_monitor_settings_list(self) -> dict[str, object] | str: + api_name = 'SYNO.ResourceMonitor.Setting' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def file_handling_access(self, sort_direction: str = 'ASC', sort_by: str = 'service', limit: int = 50, + offset: int = 0) -> dict[str, object] | str: + api_name = 'SYNO.Core.FileHandle' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'forceReload': 'true', 'action': 'enum', + 'sort_direction': sort_direction, 'sort_by': sort_by, 'limit': limit, 'offset': offset} + + return self.request_data(api_name, api_path, req_param) + + def list_service_group(self, interval=0) -> dict[str, object] | str: + api_name = 'SYNO.Core.System.ProcessGroup' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'node': 'xnode-3697', 'interval': interval} + + return self.request_data(api_name, api_path, req_param) + + def list_process_group(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System.Process' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def installed_package_list(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Package' + info = self.core_list[api_name] + api_path = info['path'] + additional = ["description", "description_enu", "dependent_packages", "beta", "distributor", "distributor_url", + "maintainer", "maintainer_url", "dsm_apps", "dsm_app_page", "dsm_app_launch_name", + "report_beta_url", + "support_center", "startable", "installed_info", "support_url", "is_uninstall_pages", + "install_type", + "autoupdate", "silent_upgrade", "installing_progress", "ctl_uninstall", "updated_at", "status", + "url", + "available_operation"] + + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': additional} + + return self.request_data(api_name, api_path, req_param) + + def active_notifications(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.DSMNotify' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'notify', 'action': 'load'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/dhcp_server.py b/synology_api/dhcp_server.py new file mode 100644 index 0000000..33b3e9b --- /dev/null +++ b/synology_api/dhcp_server.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import Optional + +from . import base_api + + +class DhcpServer(base_api.BaseApi): + + def general_info(self, ifname:str = 'ovs_eth0') -> dict[str, object] | str: + api_name = 'SYNO.Network.DHCPServer' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'ifname': ifname} + + return self.request_data(api_name, api_path, req_param) + + def vendor(self) -> dict[str, object] | str: + api_name = 'SYNO.Network.DHCPServer.Vendor' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def pxe(self) -> dict[str, object] | str: + api_name = 'SYNO.Network.DHCPServer.PXE' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def tftp(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.TFTP' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network_bond(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Bond' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def network_ethernet(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Network.Ethernet' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def dhcp_clientlist(self, ifname:str = 'bond0') -> dict[str, object] | str: + api_name = 'SYNO.Network.DHCPServer.ClientList' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'ifname': ifname} + + return self.request_data(api_name, api_path, req_param) + + def dhcp_reservations(self, ifname:str = 'bond0') -> dict[str, object] | str: + api_name = 'SYNO.Network.DHCPServer.Reservation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'ifname': ifname} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/directory_server.py b/synology_api/directory_server.py new file mode 100644 index 0000000..bb9e4da --- /dev/null +++ b/synology_api/directory_server.py @@ -0,0 +1,798 @@ +"""directory_server.py works with base_api_core to provide AD capabilities.""" +from __future__ import annotations +import json +from typing import List, Optional, Any +from . import base_api + + +class DirectoryServer(base_api.BaseApi): + """The directory server API. + + Not all items within this class use the Active Directory API. Some use the Synology Entry API which proxies + the request. Some are related to managing users in ways that are useful in the Directory Server context. For + example, sending a user password reset email, or updating the user information. This api works slightly + differently than other similar APIs. There are multi-leveled calls where Synology makes requests on behalf of + the original request and relays information back. Additionally, the query-string api item is not used often in + this class as API is defined within the actual request. + + The APIs in this class are tested working against the following scenarios: + - Get Active Directory information + - List objects within a Base DN on the Active Directory Server + - Create an AD user + - Set the user's AD password + - Send a password reset email to any Synology user + - Create a new AD group + - Add an AD user to an AD group + - Check if an AD object within your Directory Server + - Update user information within the Directory Server + - Update Synology's awareness of the current state of the Domain + - Get the status of a running task such as the Domain status update + - Delete a list of items from the Directory Server + - Delete a single item from the Directory Server + - Perform an entry request to complete a Deletion + """ + + def get_directory_info(self) -> dict[str, object] | str: + """ + Gets directory info. + + Returns + ------- + Information about your domain. Example below. + { + "data": { + "data": { + "domainBasicInfo": { + "realm": "MY.DOMAIN.COM", + "workgroup": "NETBIOSNAME" + }, + "domainControllers": [ + { + "cn": "AD", + "dn": "CN=AD,OU=Domain Controllers,DC=MY,DC=DOMAIN,DC=COM", + "dnshostname": "AD.MY.DOMAIN.COM", + "roles": [ + "pdc", + "rid", + "schema", + "naming", + "infrastructure" + ] + } + ] + }, + "status": "running" + }, + "success": true + } + """ + api_name = 'SYNO.ActiveDirectory.Info' + info = {'maxVersion': 3, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'api': api_name, 'method': 'get', + 'version': info['maxVersion']} + return self.request_data(api_name, api_path, req_param) + + def list_directory_objects(self, + basedn: str, + offset: int = 0, + limit: int = 40, + objectCategory: list[str] = [ "person", "group", "organizationalUnit", "computer", "container", "builtinDomain"] + ) -> dict[str, object] | str: + """ + lists directory objects. + + Parameters + ---------- + basedn : str + The Base DN for the search. eg. "CN=Users,CN=MY,CN=DOMAIN,CN=COM" or CN=MY,CN=DOMAIN,CN=COM + + offset : Optional, int + When searching large data, you may wish to start at a certain number, e.g. for 10 at a time one + would set the limit to 10 and the offset by multiples of 10 for each request. + Default: 0 + limit : Optional, int + The numeric the number of maximum objects to return. + Default: 40 + objectCategory : Optional, str([]) + The categories of items to search. e.g. ["organizationalUnit","container","builtinDomain"] for a list of + base server containers, and ["person","group","organizationalUnit","computer"] for a list of contained objects. + Default: ["person","group","organizationalUnit","computer","container","builtinDomain"] + + Returns + ------- + dictionary + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + { + "data": { + "data": [ + { + "accountExpiryTime": 910692730085, + "deletable": true, + "description": "This is a description of a user person", + "disabled": false, + "displayName": "John Doe", + "dn": "CN=jdoe,CN=Users,DC=MY,DC=DOMAIN,DC=COM", + "locked": false, + "mail": "jdoe@MY.EMAIL.COM", + "movable": true, + "name": "john", + "objectCategory": "person", + "passwordExpired": true, + "physicalDeliveryOfficeName": "official office of officers", + "primaryGroupToken": 0, + "renamable": true, + "sAMAccountName": "jdoe", + "showInAdvancedViewOnly": false, + "telephoneNumber": "123-444-5677" + }, + ], + "total": 99999 + }, + "success": true + } + """ + action = '"enum"' + scope = '"one"' + api = 'SYNO.ActiveDirectory.Directory' + method = '"list"' + info = {'maxVersion': 3, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'api': api, 'action': action, 'basedn': '"'+basedn+'"', 'limit': limit, "method": method, + 'objectCategory': json.dumps(objectCategory), 'offset': offset, 'scope': scope, 'version': info['maxVersion']} + print(req_param) + return self.request_data("", api_path, req_param, 'post') + + def create_new_user( + self, + logon_name: str, + email: str, + password: str, + located_dn: str, + description: str = '', + account_is_disabled: str = 'false', + cannot_change_password: str = 'false', + change_password_next_logon: str = 'null', + password_never_expire: str = 'true' + ) -> List[str]: + """Create a new user. + + Please note that synchronization with Synology is a separate step. The user can be created in AD, but not able to log on until the next synchronization occurs. + + Parameters + ---------- + logon_name : str + The desired username. "jdoe" + email: str + The desired email + password : str + The plain-text password for the new user. "Password123" + located_dn : str + The DN for the user. "CN=Users,CN=MY,CN=DOMAIN,CN=COM" + description : str, optional + A description for the user + account_is_disabled : str + Set to 'true' if the account should be disabled (default is false) + cannot_change_password : str, optional + Set to 'true' if the user cannot change the password (default is false) + change_password_next_logon : str, optional + Set to 'true' if the user must change password on next logon (default is false) + cannot_change_password : str, optional + Set to 'true' if the user cannot change the password (default is false) + password_never_expire: str + Pwd Never Expire + + Returns + ------- + dictionary + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. The + data dictionary contains an 'error', or it contains a 'dn' and a 'name'. here is an example of a successful + result. + {'data': {'dn': 'CN=jdoe,CN=Users,DC=MY,DC=DOMAIN,DC=COM', 'name': 'NETBIOSNAME\\ababab'}, 'success': True} + """ + + api_name = "SYNO.ActiveDirectory.User" + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'api': api_name, 'version': info['maxVersion'], 'method': 'create', 'logon_name': logon_name, 'email': email, + 'located_dn': located_dn, 'password': password, 'description': description, 'account_is_disabled': account_is_disabled, + 'cannot_change_password': cannot_change_password, 'change_password_next_logon': change_password_next_logon, + 'password_never_expire': password_never_expire} + return self.request_data(api_name, api_path, req_param) + + def reset_password(self, + username: str, + ) -> List[str]: + """ + Send a password reset email. + + This will trigger the password reset email from + Control Panel>Notification>Rules>System>Reset password for your account to be sent + to the user. In order to use this, + Control Panel>User & Group>Advanced>"Allow non-administrator users to reset forgotten passwords via email" + must be enabled. + + Parameters + ---------- + username : str + The username to reset. E.g. "My Group" + + Returns + ------- + dictionary + The return object can be checked for the "success" to be a true or false. + True indicates a successful operation. + + {"data": {"msg": 3}, "success": true} + + """ + + api_name = 'SYNO.Auth.ForgotPwd' + newApi = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + info = newApi + api_path = info['path'] + req_param = {'method': 'send', 'user': '"' + + username+'"', 'version': newApi['maxVersion']} + return self.request_data(api_name, api_path, req_param) + + def change_user_password(self, user_dn: str, password: str) -> dict[str, object] | str: + """ + Change the user's password. This is a compound dual-level request where the synology API proxies your + request to the Directory Server. + + Parameters + ---------- + user_dn: str + The user DN to be modified. eg. "CN=jdoe,CN=Users,DC=MY,DC=DOMAIN,DC=COM" + password: str + The new password to be set. e.g. "Password123" + + Returns + ------- + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + { + "data": { + "has_fail": false, + "result": [ + { + "api": "SYNO.ActiveDirectory.User", + "data": [ + { + "code": 0, + "msg": "update record successfully" + } + ], + "method": "set", + "success": true, + "version": 2 + } + ] + }, + "success": true + } + + """ + api_name = "SYNO.Entry.Request" + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + compound = '[{"api":"SYNO.ActiveDirectory.User","method":"set","version":2,"userList":[{"dn":"' + \ + user_dn+'","enbl_change_password":true,"password":"'+password+'"}]}]' + api_path = info['path'] + req_param = {"api": api_name, 'method': 'request', 'compound': compound, + 'mode': 'sequential', 'stop_when_error': "true", 'version': info['maxVersion']} + return self.request_data(api_name, api_path, req_param) + + def create_new_group( + self, + name: str, + located_dn: str, + email: Optional[str] = '', + description: Optional[str] = '', + type: Optional[str] = 'security', + scope: Optional[str] = 'global' + ) -> List[str]: + """ + Create a new AD group. + + Parameters + ---------- + name : str + The name of the group. E.g. "My Group" + located_dn : str + The DN to place the group in. eg. "CN=Groups,DC=MY,DC=DOMAIN,DC=COM" + email : str, Optional + The email address used to reference this group. + Default: "" + description : str, Optional + A description of the AD Group. + Default: Empty + type : str, Optional + Example Options: security, distribution + + (definitions from + https://docs.microsoft.com/en-us/microsoft-365/admin/create-groups/compare-groups?view=o365-worldwide + ) + - distribution (Distribution groups) are used for sending email + notifications to a group of people. + - security - Security groups are used for granting access to resources + such as SharePoint sites. + + Default: security + scope : str, Optional + Example Options : local, global, universal + (Definitions from + https://www.netwrix.com/active_directory_group_management.html ) + - local (Domain Local Groups) should be used to manage permissions to + resources because this group can be applied everywhere in the domain. + A domain local group can include members of any type in the domain and + members from trusted domains. For example, suppose you need access + management for a collection of folders on one or more servers that + contain information for managers. The group you create for that purpose + should be a domain local group (ex. “DL_Managers_Modify”). + - global (Global Groups) are used primarily to define collections of + domain objects (users, other global groups and computers) based on + business roles, which means that they mostly serve as role groups. + Role-based groups of users (such as “HR” or “Marketing”) and role-based + groups of computers (such as a “Marketing Workstations”) areusually + global groups. + - universal (Universal Groups) in Active Directory are useful in + multi-domain forests. They enable you to define roles or manage + resources that span more than one domain. Each universal group is + stored in the domain of where it was created, but its group membership + is stored in the Global Catalog and replicated forest-wide. Don’t use + universal groups if you have only one domain. + + Default: global + + Returns + ------- + A success object, and data object containing the new dn and the netbios name of the group. + {'data': {'dn': 'CN=My Group,CN=Groups,DC=MY,DC=DOMAIN,DC=COM', 'name': 'NETBIOSNAME\\My Group'}, 'success': True} + """ + api_name = 'SYNO.ActiveDirectory.Group' + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'api': api_name, 'method': 'create', 'name': name, 'located_dn': located_dn, + 'description': description, 'type': type, 'scope': scope, 'email': email, 'version': info['maxVersion']} + return self.request_data(api_name, api_path, req_param) + + def add_user_to_group(self, userDn: str, groupDn: str) -> dict[str, object] | str: + """ + Adds a user as a member of a group. + + + Parameters + ---------- + userDn : str + The fully qualified dn to add. eg. "CN=jdoe,CN=Users,CN=MY,CN=DOMAIN,CN=COM" + + groupDn : str + the fully qualified dn of the group to which the user is to be added. + e.g. "CN=My Group,CN=Groups,CN=MY,CN=DOMAIN,CN=COM" + + Returns + ------- + dictionary + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + { + "data": { + "has_fail": false, + "result": [ + { + "api": "SYNO.ActiveDirectory.Group.Member", + "data": { + "members": [ + "CN=jdoe,CN=Users,CN=MY,CN=DOMAIN,CN=COM" + ] + }, + "method": "add", + "success": true, + "version": 1 + } + ] + }, + "success": true + } + + + """ + + api_name = 'SYNO.Entry.Request' + compound = '[{"api":"SYNO.ActiveDirectory.Group.Member","method":"add","version":"1","dn":"' + \ + groupDn+'","members":["'+userDn+'"]}]' + method = 'request' + mode = "sequential" + stop_when_error = True + newApi = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + info = newApi + api_path = info['path'] + req_param = {'api': api_name, 'compound': compound, 'method': method, 'mode': mode, + 'stop_when_error': stop_when_error, 'version': newApi['maxVersion']} + return self.request_data(api_name, api_path, req_param) + + def does_dn_exist(self, groupName: str) -> dict[str, object] | str: + """Checks if a container exists. This can be used to verifiy the username or group name is unique. This will + not check the container, only if a similarly named container already exists. + + Parameters + ---------- + groupName : str + The user, or group's name. e.g. "jdoe" or "My Cool Group" + Fully Qualified Domain Name such as "CN=My Cool Group,CN=Groups,DC=MY,DC=DOMAIN,DC=COM" are not successful + Improper case such as "my cool group" instead of "My Cool Group" are successful + + Returns + ------- + boolean + True if the group exists. False if the group does not exist + """ + + api_name = 'SYNO.ActiveDirectory.Group' + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'version': info['maxVersion'], + 'method': 'conflict', 'name': groupName} + return self.request_data(api_name, api_path, req_param)['data']['isConflict'] + + def modify_user_info(self, + user_dn: str = None, + firstName: str = None, + lastName: str = None, + displayName: str = None, + description: str = None, + initials: str = None, + physicalDeliveryOfficeName: str = None, + telephoneNumber: str = None, + web: str = None + ) -> Any: + """ + Performs modification to user information within the Active Directory. + + Parameters + ---------- + user_dn: str + The user DN to be modified. eg. "CN=jdoe,CN=Users,DC=MY,DC=DOMAIN,DC=COM" + firstName: Optional, str + The First name of the user. e.g. "John" + lastName: Optional, str + The Last Name of the user. e.g. "Doe" + displayName: Optional, str + The Display name of the user. e.g. "John Doe" + description: Optional, str + The Descrition of the user. e.g. "The guy who just came in" + initials: Optional, str + The Initials of the user. e.g. "JD" + physicalDeliveryOfficeName: Optional, str + The office location in the user's place of business + telephoneNumber: Optional, str + The user's telephone number. + web: Optional, str + The user's website or location on the web where information can be obtained. + + Returns + ------- + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + { + "data": { + "has_fail": true, + "result": [ + { + "api": "SYNO.ActiveDirectory.User", + "error": { + "code": 10104, + "errors": [ + { + "code": 10237, + "msg": "ldb updaterecords: modify" + } + ] + }, + "method": "set", + "success": false, + "version": 2 + } + ] + }, + "success": true + } + """ + class Person: + firstName + lastName + displayName + description + initials + physicalDeliveryOfficeName + telephoneNumber + web + user_dn + userObject = Person() + userObject.dn = user_dn + if firstName is not None: + userObject.firstName = firstName + if lastName is not None: + userObject.lastName = lastName + if displayName is not None: + userObject.displayName = displayName + #if description is not None: + # userObject.description = description + if initials is not None: + userObject.initials = initials + if physicalDeliveryOfficeName is not None: + userObject.physicalDeliveryOfficeName = physicalDeliveryOfficeName + if telephoneNumber is not None: + userObject.telephoneNumber = telephoneNumber + if web is not None: + userObject.web = web + + theJsonObject = userObject.__dict__ + val = self.setEntryRequest("SYNO.ActiveDirectory.User", "set", "userList", theJsonObject) + + return val + + def setEntryRequest(self, modificationAPI: str, method: str, nameOfObject: str, jsonObject: Any) -> dict[str, object] | str: + """ + Performs modification to an object within the Active Directory. + + Parameters + ---------- + modificationAPI: str + method: str + nameOfObject: str + The user DN to be modified. eg. "CN=jdoe,CN=Users,DC=MY,DC=DOMAIN,DC=COM" + jsonObject: str: o + the json Object to be added, eg, a user object where the + + Returns + ------- + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + { + "data": { + "has_fail": true, + "result": [ + { + "api": "SYNO.ActiveDirectory.User", + "error": { + "code": 10104, + "errors": [ + { + "code": 10237, + "msg": "ldb updaterecords: modify" + } + ] + }, + "method": "set", + "success": false, + "version": 2 + } + ] + }, + "success": true + } + """ + compound = [{"api":modificationAPI,"method": method,"version":2,nameOfObject:[jsonObject]}] + api_name = "SYNO.Entry.Request" + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {"api": api_name, 'method': 'request', 'compound': json.dumps(compound), 'mode': '"sequential"', 'stop_when_error': True, 'version': info['maxVersion']} + print (json.dumps(req_param)) + return self.request_data(api_name, api_path, req_param,"post") + + def update_domain_records(self) -> dict[str, object] | str: + """ + Updates the Synology users and groups database with information from Directory Server. + + This is a long-running and asynchronous task. You are given back a task_id, and you can + use that task_id to check the status with the get_task_status(task_id) method. + + Returns + ------- + dictionary + The 'data' object contains the 'task_id' used to track with the getTaskStatus() method. + The 'success' object will be true if the operation was successful. or false if failed. + + {"data": {"task_id": "@administrators/DomainUpdate6146195136397F2"}, "success": true} + + Note + ---- + Typical utilization of Update Domain requires starting the update job and waiting for + completion. Waiting involves using the getTaskStatus and can be accomplished via a busy-wait + method such as the following: + + updateResponse=directory.updateDomain() + status=directory.getTaskStatus(updateResponse['data']['task_id']) + while status['data']['status'] == 'updating' : + status=directory.getTaskStatus(updateResponse['data']['task_id']) + """ + api_name = 'SYNO.Core.Directory.Domain' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'domain_name': '"@all"', + 'method': 'update_start', 'version': info['minVersion']} + return self.request_data(api_name, api_path, req_param) + + def get_task_status(self, task_id: str) -> dict[str, object] | str: + """ + Gets the current status of a task running on the Directory Domain object. + + This is used to ensure the task is completed. For example, the primary utilization of this is + when updating Synology's internal Domain user and group list. Until this method reports + finish, the job is not completed, and it is not safe to operate under the assumption that users + have been synchronized. + + Parameters + ---------- + task_id : str + The task ID to be tracked for status. + + Returns + ------- + dictionary + The 'data' object contains the 'status' used to determine the current status. 'status' + will be 'updating' or 'finish' if the job was started. + The 'success' object will be true if the operation was successful. or false if failed. + + {'data': {'status': 'updating'}, 'success': True} + """ + + api_name = 'SYNO.Core.Directory.Domain' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'method': 'update_status', + 'task_id': task_id, 'version': info['minVersion']} + return self.request_data(api_name, api_path, req_param) + + def deleteItems(self, dnList: list[str]) -> dict[str, object] | str: + """ + Deletes an array of DNs from AD. + + Parameters + ---------- + dnList : str([]) + The fully qualified DN to be removed from the directory server. + eg. ["CN=jdoe,CN=Users,CN=MY,CN=DOMAIN,CN=COM","CN=My Group,CN=Groups,CN=MY,CN=DOMAIN,CN=COM"] + + Returns + ------- + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + + { + "data": { + "has_fail": false, + "result": [ + { + "api": "SYNO.ActiveDirectory.Polling", + "data": { + "data": [ + { + "dn": "CN=My Group,CN=Groups,CN=MY,CN=DOMAIN,CN=COM", + "status": {} + } + ], + "finished": true, + "total": 1 + }, + "method": "get", + "success": true, + "version": 1 + } + ] + }, + "success": true + } + """ + api_name = 'SYNO.ActiveDirectory.Directory' + info = {'maxVersion': 2, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + api_path = info['path'] + req_param = {'api': api_name, 'method': 'delete', + 'dnList': json.dumps(dnList), 'version': 2} + task_id = self.request_data(api_name, api_path, req_param)[ + 'data']['task_id'] + returnValue = self.entryRequest(task_id) + notFinished = True + while 'data' in returnValue['data']['result'][0] and notFinished: + notFinished = False + for resultItem in returnValue['data']['result']: + if not resultItem['data']['finished']: + notFinished = True + if not notFinished: + break + returnValue = self.entryRequest(task_id) + + return returnValue + + def delete_item(self, dn: str) -> dict[str, object] | str: + """ + Deletes a DN from AD. + + Parameters + ---------- + dn : str + The fully qualified DN to be removed from the directory server. + eg. "CN=jdoe,CN=Users,CN=MY,CN=DOMAIN,CN=COM" or + "CN=My Group,CN=Groups,CN=MY,CN=DOMAIN,CN=COM" + + Returns + ------- + The result of this method is a dictionary object with a 'data' dictionary and a 'success' dictionary. + The first level is the success to the AD server. The second Data level is the status of the actual request. + Since this is a compound request, the data contains an object with it's own request and results contained + within. The object will explain any issues with the request. The data structure is as follows: + + { + "data": { + "has_fail": false, + "result": [ + { + "api": "SYNO.ActiveDirectory.Polling", + "data": { + "data": [ + { + "dn": "CN=My Group,CN=Groups,CN=MY,CN=DOMAIN,CN=COM", + "status": {} + } + ], + "finished": true, + "total": 1 + }, + "method": "get", + "success": true, + "version": 1 + } + ] + }, + "success": true + } + """ + items = [] + items.append(dn) + return self.deleteItems(items) + + def entryRequest(self, task_id: str) -> Any: + """ + Some requests require an entry. + + Delete for example requires an entry. If an entry is required, the task will not + complete without an Entry Request. + + Parameters + ---------- + task_id: str + The ID of the task to be checked. This is provided when making a request. An example Task ID may look like this + "@administrators/Synoads_SYNO.ActiveDirectory.Directory_delete6145EA17C4F03DA9" + """ + api_name = 'SYNO.Entry.Request' + info = {'maxVersion': 1, 'minVersion': 1, + 'path': 'entry.cgi', 'requestFormat': 'JSON'} + compound = '[{"api":"SYNO.ActiveDirectory.Polling","method":"get","version":1,"task_id":"'+task_id+'"}]' + api_path = info['path'] + req_param = {'api': api_name, 'method': 'request', 'compound': compound, + 'mode': 'parallel', 'version': info['maxVersion']} + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/docker_api.py b/synology_api/docker_api.py new file mode 100644 index 0000000..a1f923c --- /dev/null +++ b/synology_api/docker_api.py @@ -0,0 +1,55 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class Docker(base_api.BaseApi): + + def containers(self) -> dict[str, object] | str: + api_name = 'SYNO.Docker.Container' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'limit': '-1', 'offset': '0', 'type': 'all'} + + return self.request_data(api_name, api_path, req_param) + + def container_resources(self) -> dict[str, object] | str: + api_name = 'SYNO.Docker.Container.Resource' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def system_resources(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.System.Utilization' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def downloaded_images(self) -> dict[str, object] | str: + api_name = 'SYNO.Docker.Image' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'limit': '-1', 'offset': '0', + "show_dsm": 'false'} + + return self.request_data(api_name, api_path, req_param) + + def images_registry_resources(self) -> dict[str, object] | str: + api_name = 'SYNO.Docker.Registry' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def network(self) -> dict[str, object] | str: + api_name = 'SYNO.Docker.Network' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/downloadstation.py b/synology_api/downloadstation.py new file mode 100644 index 0000000..13141be --- /dev/null +++ b/synology_api/downloadstation.py @@ -0,0 +1,344 @@ +from __future__ import annotations +from typing import Optional, Any +from . import base_api + + +class DownloadStation(base_api.BaseApi): + + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None, + interactive_output: bool = True, + download_st_version: int = None + ) -> None: + + super(DownloadStation, self).__init__(ip_address, port, username, password, secure, cert_verify, + dsm_version, debug, otp_code, 'DownloadStation') + + self._bt_search_id: str = '' + self._bt_search_id_list: list[str] = [] + self.session.get_api_list('DownloadStation') + + self.download_list: Any = self.session.app_api_list + + self.interactive_output: bool = interactive_output + + if download_st_version == 2: + self.download_st_version = '2' + else: + self.download_st_version = '' + + def get_info(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.Info' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo'} + + return self.request_data(api_name, api_path, req_param) + + def get_config(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.Info' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getconfig'} + + return self.request_data(api_name, api_path, req_param) + + def set_server_config(self, + bt_max_download: Optional[int] = None, + bt_max_upload: Optional[int] = None, + emule_max_download: Optional[int] = None, + emule_max_upload: Optional[int] = None, + nzb_max_download: Optional[int] = None, + http_max_download: Optional[int] = None, + ftp_max_download: Optional[int] = None, + emule_enabled: Optional[bool] = None, + unzip_service_enabled: Optional[bool] = None, + default_destination: Optional[str] = None, + emule_default_destination: Optional[str] = None + ) -> dict[str, object] | str: + + api_name = 'SYNO.DownloadStation.Info' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'setserverconfig'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def schedule_info(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.Schedule' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getconfig'} + + return self.request_data(api_name, api_path, req_param) + + def schedule_set_config(self, enabled: bool = False, emule_enabled: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.Schedule' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'setconfig', 'enabled': str(enabled).lower(), + 'emule_enabled': str(emule_enabled).lower()} + + if type(enabled) is not bool or type(emule_enabled) is not bool: + return 'Please set enabled to True or False' + + return self.request_data(api_name, api_path, req_param) + + def tasks_list(self, + additional_param: Optional[str | list[str]] = None, + offset: int = 0, + limit: int = -1 + ) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': additional_param, 'limit': limit, + 'offset': offset} + + if additional_param is None: + additional_param = ['detail', 'transfer', 'file', 'tracker', 'peer'] + + if type(additional_param) is list: + req_param['additional'] = ",".join(additional_param) + + return self.request_data(api_name, api_path, req_param) + + def tasks_info(self, task_id, additional_param: Optional[str | list[str]] = None) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo', 'id': task_id, 'additional': additional_param} + + if additional_param is None: + additional_param = ['detail', 'transfer', 'file', 'tracker', 'peer'] + + if type(additional_param) is list: + req_param['additional'] = ",".join(additional_param) + + if type(task_id) is list: + req_param['id'] = ",".join(task_id) + + return self.request_data(api_name, api_path, req_param) + + def tasks_source(self, task_id) -> bytes: + # DownloadStation2 is required here + api_name = 'SYNO.DownloadStation2.Task.Source' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'download', 'id': task_id} + + return self.request_data(api_name, api_path, req_param, response_json=False).content + + def create_task(self, uri, additional_param: Optional[dict[str, object]] = None) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create', 'uri': uri} + + if type(additional_param) is dict: + for key in additional_param.keys(): + req_param[key] = additional_param[key] + + return self.request_data(api_name, api_path, req_param) + + def delete_task(self, task_id: str, force: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'delete', 'id': task_id, + 'force_complete': str(force).lower()} + + if type(task_id) is list: + param['id'] = ",".join(task_id) + + return self.request_data(api_name, api_path, param) + + def pause_task(self, task_id: str) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'pause', 'id': task_id} + + if type(task_id) is list: + param['id'] = ",".join(task_id) + + return self.request_data(api_name, api_path, param) + + def resume_task(self, task_id: str) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'resume', 'id': task_id} + + if type(task_id) is list: + param['id'] = ",".join(task_id) + + return self.request_data(api_name, api_path, param) + + def edit_task(self, task_id: str, destination: str = 'sharedfolder') -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.Task' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'edit', 'id': task_id, 'destination': destination} + + if type(task_id) is list: + param['id'] = ",".join(task_id) + + return self.request_data(api_name, api_path, param) + + def get_statistic_info(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.Statistic' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'getinfo'} + + return self.request_data(api_name, api_path, param) + + def get_rss_info_list(self, offset: Optional[int] = None, limit: Optional[int] = None) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.RSS.Site' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'list'} + + if offset is not None: + param['offset'] = offset + if limit is not None: + param['limit'] = limit + + return self.request_data(api_name, api_path, param) + + def refresh_rss_site(self, rss_id: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation.RSS.Site' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'refresh', 'id': rss_id} + + if rss_id is None: + return 'Enter a valid ID check if you have any with get_rss_list()' + elif type(rss_id) is list: + rss_id = ','.join(rss_id) + param['id'] = rss_id + + return self.request_data(api_name, api_path, param) + + def rss_feed_list(self, + rss_id: Optional[str] = None, + offset: Optional[int] = None, + limit: Optional[int] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.RSS.Feed' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'list', 'id': rss_id} + + if rss_id is None: + return 'Enter a valid ID check if you have any with get_rss_list()' + elif type(rss_id) is list: + rss_id = ','.join(rss_id) + param['id'] = rss_id + + if offset is not None: + param['offset'] = offset + if limit is not None: + param['limit'] = limit + + return self.request_data(api_name, api_path, param) + + def start_bt_search(self, keyword: Optional[str] = None, module: str = 'all') -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.BTSearch' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'start'} + + if keyword is None: + return 'Did you enter a keyword to search?' + else: + param['keyword'] = keyword + + param['module'] = module + + self._bt_search_id = self.request_data(api_name, api_path, param)['data']['taskid'] + + self._bt_search_id_list.append(self._bt_search_id) + + message = ('You can now check the status of request with ' + 'get_bt_search_results(), your id is: ' + + self._bt_search_id) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._bt_search_id} + + return output + + def get_bt_search_results(self, + taskid: Optional[str] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + filter_category: Optional[str] = None, + filter_title: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.BTSearch' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'list', 'taskid': taskid} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'param', 'taskid']: + if val is not None: + param[str(key)] = val + + if taskid is None: + return 'Enter a valid taskid, you can choose one of ' + str(self._bt_search_id_list) + elif type(taskid) is list: + param['taskid'] = ','.join(taskid) + + return self.request_data(api_name, api_path, param) + + def get_bt_search_category(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.BTSearch' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, param) + + def clean_bt_search(self, taskid: Optional[str | list[str]] = None) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.BTSearch' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'clean', 'taskid': taskid} + + if taskid is None: + return 'Enter a valid taskid, you can choose one of ' + str(self._bt_search_id_list) + elif type(taskid) is list: + param['taskid'] = ','.join(taskid) + for item in taskid: + self._bt_search_id_list.remove(item) + else: + self._bt_search_id_list.remove(taskid) + + return self.request_data(api_name, api_path, param) + + def get_bt_module(self) -> dict[str, object] | str: + api_name = 'SYNO.DownloadStation' + self.download_st_version + '.BTSearch' + info = self.download_list[api_name] + api_path = info['path'] + param = {'version': info['maxVersion'], 'method': 'getModule'} + + return self.request_data(api_name, api_path, param) diff --git a/synology_api/drive_admin_console.py b/synology_api/drive_admin_console.py new file mode 100644 index 0000000..5ae1be6 --- /dev/null +++ b/synology_api/drive_admin_console.py @@ -0,0 +1,146 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class AdminConsole(base_api.BaseApi): + + def status_info(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_status'} + + return self.request_data(api_name, api_path, req_param) + + def config_info(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def connections(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Connection' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'summary'} + + return self.request_data(api_name, api_path, req_param) + + def drive_check_user(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'check_user'} + + return self.request_data(api_name, api_path, req_param) + + def active_connections(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Connection' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def active_sync_connections(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDriveShareSync.Connection' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def share_active_list(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Share' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list_active'} + + return self.request_data(api_name, api_path, req_param) + + def log(self, + share_type: str = 'all', + get_all: bool = False, + limit: int = 1000, + keyword: str = '', + date_from: int = 0, + date_to: int = 0, + username: str = '', + target: str = 'user' + ) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Log' + info = self.gen_list[api_name] + api_path = info['path'] + + if get_all: + get_all = 'true' + elif not get_all: + get_all = 'false' + else: + return 'get_all must be True or False' + + req_param = {'version': info['maxVersion'], 'method': 'list', 'share_type': share_type, 'get_all': get_all, + 'limit': limit, 'keyword': keyword, 'datefrom': date_from, 'dateto': date_to, 'username': username, + 'target': target} + + return self.request_data(api_name, api_path, req_param) + + def c2fs_share(self) -> dict[str, object] | str: + api_name = 'SYNO.C2FS.Share' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def settings(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Settings' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def db_usage(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.DBUsage' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def delete_status(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Node.Delete' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + return self.request_data(api_name, api_path, req_param) + + def file_property_transfer_status(self) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Migration.UserHome' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + return self.request_data(api_name, api_path, req_param) + + def user_sync_profile(self, user: str = '', start: int = 0, limit: str | int = 'null') -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Profiles' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'start': start, 'limit': limit, 'user': user} + + return self.request_data(api_name, api_path, req_param) + + def index_pause(self, time_pause: int = 60) -> dict[str, object] | str: + api_name = 'SYNO.SynologyDrive.Index' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set_native_client_index_pause', + 'pause_duration': time_pause} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/error_codes.py b/synology_api/error_codes.py new file mode 100644 index 0000000..9714db6 --- /dev/null +++ b/synology_api/error_codes.py @@ -0,0 +1,228 @@ +# source: pages 8 and 16 on PDF: +# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf + +# NOTE: https://global.synologydownload.com/download/Document/Software/DeveloperGuide/Package/Calendar/2.4/enu +# /Synology_Calendar_API_Guide_enu.pdf, Refers to common error code # 160, "Insufficient application privilege" Page 10. +from __future__ import annotations + +CODE_SUCCESS = 0 +CODE_UNKNOWN = 9999 +# 'Common' Error Codes: +error_codes = { + CODE_SUCCESS: 'Success', + 100: 'Unknown error', + 101: 'No parameter of API, method or version', + 102: 'The requested API does not exist', + 103: 'The requested method does not exist', + 104: 'The requested version does not support the functionality', + 105: 'The logged in session does not have permission', + 106: 'Session timeout', + 107: 'Session interrupted by duplicated login', + 108: 'Failed to upload the file', + 109: 'The network connection is unstable or the system is busy', + 110: 'The network connection is unstable or the system is busy', + 111: 'The network connection is unstable or the system is busy', + 112: 'Preserve for other purpose', + 113: 'Preserve for other purpose', + 114: 'Lost parameters for this API', + 115: 'Not allowed to upload a file', + 116: 'Not allowed to perform for a demo site', + 117: 'The network connection is unstable or the system is busy', + 118: 'The network connection is unstable or the system is busy', + 119: 'Invalid session / SID not found.', + # 120-149 Preserve for other purpose + 120: 'Preserve for other purpose', + 121: 'Preserve for other purpose', + 122: 'Preserve for other purpose', + 123: 'Preserve for other purpose', + 124: 'Preserve for other purpose', + 125: 'Preserve for other purpose', + 126: 'Preserve for other purpose', + 127: 'Preserve for other purpose', + 128: 'Preserve for other purpose', + 129: 'Preserve for other purpose', + 130: 'Preserve for other purpose', + 131: 'Preserve for other purpose', + 132: 'Preserve for other purpose', + 133: 'Preserve for other purpose', + 134: 'Preserve for other purpose', + 135: 'Preserve for other purpose', + 136: 'Preserve for other purpose', + 137: 'Preserve for other purpose', + 138: 'Preserve for other purpose', + 139: 'Preserve for other purpose', + 140: 'Preserve for other purpose', + 141: 'Preserve for other purpose', + 142: 'Preserve for other purpose', + 143: 'Preserve for other purpose', + 144: 'Preserve for other purpose', + 145: 'Preserve for other purpose', + 146: 'Preserve for other purpose', + 147: 'Preserve for other purpose', + 148: 'Preserve for other purpose', + 149: 'Preserve for other purpose', + 150: 'Request source IP does not match the login IP', + 160: 'Insufficient application privilege', + CODE_UNKNOWN: 'Unknown Error', +} +# Source: https://global.synologydownload.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu +# /DSM_Login_Web_API_Guide_enu.pdf Page 16. +# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Package/SurveillanceStation/All/enu +# /Surveillance_Station_Web_API.pdf Pages 32,33 Refers to Auth error code #411: 'Account Locked (when account max try +# exceed).' +auth_error_codes: dict[int, str] = { + 400: 'No such account or incorrect password', + 401: 'Disabled account', + 402: 'Denied permission', + 403: '2 - factor authentication code required', + 404: 'Failed to authenticate 2 - factor authentication code', + 406: 'Enforce to authenticate with 2 - factor authentication code', + 407: 'Blocked IP source', + 408: 'Expired password cannot change', + 409: 'Expired password', + 410: 'Password must be changed', + 411: 'Account Locked (when account max try exceed).', +} + +# Source:https://global.download.synology.com/download/Document/Software/DeveloperGuide/Package/DownloadStation/All/enu/Synology_Download_Station_Web_API.pdf +# Page 29 +download_station_error_codes: dict[int, str] = { + 400: 'File upload failed', + 401: 'Max number of tasks reached', + 402: 'Destination denied', + 403: 'Destination does not exist', + 404: 'Invalid task id', + 405: 'Invalid task action', + 406: 'No default destination', + 407: 'Set destination failed', + 408: 'File does not exist', +} + +# TODO use the error code source https://cndl.synology.cn/download/Document/Software/DeveloperGuide/Package +# /FileStation/All/enu/Synology_File_Station_API_Guide.pdf page 10~11 +file_station_error_codes: dict[int, str] = { + 400: 'Invalid parameter of file operation', + 401: 'Unknown error of file operation', + 402: 'System is too busy', + 403: 'Invalid user does this file operation', + 404: 'Invalid group does this file operation', + 405: 'Invalid user and group does this file operation', + 406: "Can't get user/group information from the account server", + 407: 'Operation not permitted', + 408: 'No such file or directory', + 409: 'Non-supported file system', + 410: 'Failed to connect internet-based file system (e.g., CIFS)', + 411: 'Read-only file system', + 412: 'Filename too long in the non-encrypted file system', + 413: 'Filename too long in the encrypted file system', + 414: 'File already exists', + 415: 'Disk quota exceeded', + 416: 'No space left on device', + 417: 'Input/output error', + 418: 'Illegal name or path', + 419: 'Illegal file name', + 420: 'Illegal file name on FAT file system', + 421: 'Device or resource busy', + 599: 'No such task of the file operation', +} + +# Source: https://global.synologydownload.com/download/Document/Software/DeveloperGuide/Package/Virtualization/All +# /enu/Synology_Virtual_Machine_Manager_API_Guide.pdf Page 8,9 +virtualization_error_codes: dict[int, str] = { + 401: 'Bad parameter.', + 402: 'Operation failed.', + 403: 'Name conflict.', + 404: 'The number of iSCSI LUNs has reached the system limit. Note: vdisk is based on iSCSI LUN, which is also ' + 'limited by the system.', + 500: 'The cluster is frozen. More than half of the hosts are offline.', + 501: 'The cluster is in the incompatible mode. Please upgrade to a compatible DSM version and try again.', + 600: 'The cluster is not ready.', + 601: 'The host is offline.', + 700: 'The storage is in invalid.', + 900: 'Failed to set a host to a virtual machine.', + 901: 'The virtual machine does not have a host.', + 902: 'Failed to power on a virtual machine due to insufficient CPU threads.', + 903: 'Failed to power on a virtual machine due to insufficient memory.', + 904: 'The status of virtual machine is online.', + 905: 'MAC conflict.', + 906: 'Failed to create virtual machine because the selected image is not found.', + 907: 'The status of virtual machine is offline.', + 908: 'Failed to power on a virtual machine due to insufficient CPU threads for reservation on the host.', + 909: 'Failed to power on the virtual machine because there is no corresponding networking on the host.', + 910: 'Only the VirtIO hard disk controller can be used to boot the virtual machine remotely.', + 911: 'Virtual machines with UEFI enabled cannot be powered on remotely.', + 1000: 'Cannot find task_id.', + 1001: 'Need Virtual Machine Manager Pro.', + 1400: 'The result of image creating is partial success.', + 1600: 'The virtual machine has been successfully edited. However, errors occurred while reserving the memory or ' + 'CPU on the HA hosts.', +} + +# Source: https://global.synologydownload.com/download/Document/Software/DeveloperGuide/Package/Calendar/2.4/enu +# /Synology_Calendar_API_Guide_enu.pdf Pages 10,11. +calendar_error_codes: dict[int, str] = { + 400: 'Invalid parameter of file operation', + 401: 'Unknown error of file operation', + 402: 'System is too busy', + 403: 'This user does not have permission to execute this operation', + 404: 'This group does not have permission to execute this operation', + 405: 'This user/group does not have permission to execute this operation', + 406: 'Cannot obtain user/group information from the account server', + 407: 'Operation not permitted', + 408: 'No such file or directory', + 409: 'File system not supported', + 410: 'Failed to connect internet-based file system (ex: CIFS)', + 411: 'Read-only file system', + 412: 'Filename too long in the non-encrypted file system', + 413: 'Filename too long in the encrypted file system', + 414: 'File already exists', + 415: 'Disk quota exceeded', + 416: 'No space left on device', + 417: 'Input/output error', + 418: 'Illegal name or path', + 419: 'Illegal file name', + 420: 'Illegal file name on FAT file system', + 421: 'Device or resource busy', + 599: 'No such task of the file operation', +} + +# # Source: https://global.download.synology.com/download/Document/Software/DeveloperGuide/Package +# /SurveillanceStation/All/enu/Surveillance_Station_Web_API.pdf NOTE: Pages 81, 131,132, 155, 190, 222, 305, 314, +# 321, 328, 451, 473 539, 556, contain unique items. Most of these are duplicate information, but they describe +# different methods, all the error codes I found are on # Pages: 71, 81, 85, 93, 103, 113, 131, 132, 139, 144, 155, +# 167, 169, 176, 187, 190, 191, 201, 211, 212, 217, 222, 227, # 241, 245, 249, 253, 264, 279, 281, 305, 314, +# 321, 328, 363, 365, 368, 369, 393, 395, 397, 403, 410, 412, 415, # 419, 430, 451, 473, 539, 556 +surveillance_station_error_codes: dict[int, str] = { + 400: 'Execution failed.', + 401: 'Parameter invalid.', + 402: 'Camera disabled.', + 403: 'Insufficient license.', + 404: 'Codec activation failed', + 405: 'CMS server connection failed.', + 407: 'CMS closed.', + 412: 'Need to add license.', + 413: 'Reach the maximum of platform', + 414: 'Some events not exist.', + 415: 'message connect failed.', + 417: 'Test Connection Error.', + 418: 'Object is not exist. / The VisualStation ID does not exist.', + 419: 'Visualstation name repetition.', + 439: 'Too many items selected', + 446: 'Task Path already exist.', + 522: 'Original Task is Migrating', + 534: 'Exceed name length limitation.', + 543: 'The number of DVA tasks and face tasks exceed the limitation', + 548: 'The input video type of the DVA task is invalid', + 553: 'No face detected.', + 554: 'Face detected is too small.', + 555: 'Multiple faces detected.', + 556: 'The account try to set has exists.', + 557: 'No image data appended.', + 558: 'Add face group failed.', + 560: 'The camera try to apply to face task is occupied by another face task.', + 561: 'The number of face profile exceeds the maximum limit.', + 562: 'The number of face group exceeds the maximum limit.', + 563: 'The face profile is created or edited failed due to duplicated account.', + 564: 'The face result try to query doesn\'t exist', + 567: 'The face database is under synchronization.', +} diff --git a/synology_api/exceptions.py b/synology_api/exceptions.py new file mode 100644 index 0000000..d97e08d --- /dev/null +++ b/synology_api/exceptions.py @@ -0,0 +1,320 @@ +from .error_codes import error_codes, auth_error_codes, download_station_error_codes, file_station_error_codes +from .error_codes import virtualization_error_codes + + +# Base exception: +class SynoBaseException(Exception): + """Base class for an exception. Defines error_message.""" + + def __init__(self, error_message: str, *args: object) -> None: + super().__init__(*args) + self.error_message = error_message + return + + +# Classes to reraise Exceptions from requests. +class SynoConnectionError(SynoBaseException): + """Class to raise when a connection error occurs.""" + + def __init__(self, error_message: str, *args: object) -> None: + super().__init__(error_message=error_message, *args) + return + + +class HTTPError(SynoBaseException): + """Class to raise when a http error occurs.""" + + def __init__(self, error_message: str, *args: object) -> None: + super().__init__(error_message, *args) + return + + +class JSONDecodeError(SynoBaseException): + """Class to raise when server fails to send JSON.""" + + def __init__(self, error_message: str, *args: object) -> None: + super().__init__(error_message, *args) + return + + +# Classes for when we receive an error code in the JSON from the server. +class LoginError(SynoBaseException): + """Class for an error during login.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code: int = error_code + if error_code not in error_codes.keys(): + super().__init__(error_message=auth_error_codes[error_code], *args) + else: + super().__init__(error_message=error_codes[error_code], *args) + return + + +class LogoutError(SynoBaseException): + """Class for an error during logout.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code: int = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message=auth_error_codes[error_code], *args) + return + + +class DownloadStationError(SynoBaseException): + """Class for an error during a download station request.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code: int = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + elif error_code in download_station_error_codes.keys(): + super().__init__(error_message=download_station_error_codes[error_code], *args) + else: + super().__init__(error_message="DownloadStation Error: %i" % error_code, *args) + return + + +class FileStationError(SynoBaseException): + """Class for an error during a file station request.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code: int = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + elif error_code in file_station_error_codes.keys(): + super().__init__(error_message=file_station_error_codes[error_code], *args) + else: + super().__init__(error_message="FileStation Error: %i" % error_code, *args) + return + + +class VirtualizationError(SynoBaseException): + """Class for an error during a virtualization request.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + elif error_code in virtualization_error_codes.keys(): + super().__init__(error_message=virtualization_error_codes[error_code], *args) + else: + super().__init__(error_message="Virtualization Error: %i" % error_code, *args) + return + + +class AudioStationError(SynoBaseException): + """Class for an error during an audio station request. NOTE: I can't find any documentation on the audio station + webAPI errors numbers and their respective messages.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="AudioStation Error: %i" % error_code, *args) + return + + +class ActiveBackupError(SynoBaseException): + """Class for an error during ActiveBackup request. NOTE: I can't find any documentation on error codes or their + respective messages.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message='ActiveBackup Error: %i' % error_code, *args) + + +class BackupError(SynoBaseException): + """Class for an error during backup request. NOTE: Again I can't find error code documentation.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="Backup Error: %i" % error_code, *args) + return + + +class CertificateError(SynoBaseException): + """Class for an error during Core.Certificate request. NOTE: Lacking documentation.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code]) + else: + super().__init__(error_message="Certificate Error: %i" % error_code, *args) + return + + +class DHCPServerError(SynoBaseException): + """Class for an error during a DHCPServer request.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="DHCPServer Error: %i" % error_code, *args) + return + + +class DirectoryServerError(SynoBaseException): + """Class for an error during a directory server request. NOTE: No docs on errors.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="DirectoryServer Error: %i" % error_code, *args) + return + + +class DockerError(SynoBaseException): + """Class for an error during a docker request. NOTE: No docs on errors.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="Docker Error: %i" % error_code, *args) + return + + +class DriveAdminError(SynoBaseException): + """Class for an error during a drive admin request. NOTE: No error docs.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="DriveAdmin Error: %i" % error_code, *args) + return + + +class LogCenterError(SynoBaseException): + """Class for an error during a LogCenter request. NOTE: No docs on errors.... again.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="LogCenter Error: %i" % error_code, *args) + return + + +class NoteStationError(SynoBaseException): + """Class for an error during a NoteStation request. NOTE: No error docs.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="NoteStation Error: %i" % error_code, *args) + return + + +class OAUTHError(SynoBaseException): + """Class for an error during a OAUTH request. NOTE: No error docs.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="OAUTH Error: %i" % error_code, *args) + return + + +class PhotosError(SynoBaseException): + """Class for an error during a Photos request. NOTE: No error docs.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="Photos Error: %i" % error_code, *args) + return + + +class SecurityAdvisorError(SynoBaseException): + """Class for an error during a SecurityAdvisor request. NOTE: What docs?""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="SecurityAdvisor Error: %i" % error_code, *args) + return + + +class UniversalSearchError(SynoBaseException): + """Class for an error during UniversalSearch request. NOTE:... no docs on errors....""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="UniversalSearch Error: %i" % error_code, *args) + return + + +class USBCopyError(SynoBaseException): + """Class for an error during a USBCopy request. NOTE: No docs on errors.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="USBCopy Error: %i" % error_code, *args) + + +class VPNError(SynoBaseException): + """Class for an error during a VPN request. NOTE: No docs on errors.""" + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="VPN Error: %i" % error_code, *args) + return + + +class CoreSysInfoError(SynoBaseException): + """Class for an error during a SYNO.Core.*, 'SYNO.Backup.Service.NetworkBackup', SYNO.Storage.*, + 'SYNO.Finder.FileIndexing.Status', 'SYNO.S2S.Server.Pair', SYNO.ResourceMonitor.* + """ + + def __init__(self, error_code: int, *args: object) -> None: + self.error_code = error_code + if error_code in error_codes.keys(): + super().__init__(error_message=error_codes[error_code], *args) + else: + super().__init__(error_message="CoreSysInfo Error: %i" % error_code, *args) + return + + +class UndefinedError(SynoBaseException): + """Class for undefined errors.""" + + def __init__(self, error_code: int, api_name: str, *args: object) -> None: + self.error_code = error_code + self.api_name = api_name + super().__init__(error_message="Undefined Error: API: %s, Code: %i" % (api_name, error_code), *args) + return diff --git a/synology_api/filestation.py b/synology_api/filestation.py new file mode 100644 index 0000000..f44855a --- /dev/null +++ b/synology_api/filestation.py @@ -0,0 +1,1243 @@ +from __future__ import annotations +from typing import Optional, Any +import os +import io +import time +from datetime import datetime + +import requests +import tqdm +from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor +import sys +from urllib import parse + +from . import base_api + + +class FileStation(base_api.BaseApi): + + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None, + interactive_output: bool = True + ) -> None: + + super(FileStation, self).__init__(ip_address, port, username, password, secure, cert_verify, + dsm_version, debug, otp_code, 'FileStation') + + self._dir_taskid: str = '' + self._dir_taskid_list: list[str] = [] + self._md5_calc_taskid: str = '' + self._md5_calc_taskid_list: list[str] = [] + self._search_taskid: str = '' + self._search_taskid_list: list[str] = [] + self._copy_move_taskid: str = '' + self._copy_move_taskid_list: list[str] = [] + self._delete_taskid: str = '' + self._delete_taskid_list: list[str] = [] + self._extract_taskid: str = '' + self._extract_taskid_list: list[str] = [] + self._compress_taskid: str = '' + self._compress_taskid_list: list[str] = [] + + self.session.get_api_list('FileStation') + + self.file_station_list: Any = self.session.app_api_list + + self.interactive_output: bool = interactive_output + + def get_info(self) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Info' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def get_list_share(self, + additional: Optional[str | list[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + onlywritable: bool = False + ) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.List' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list_share'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'additional']: + if val is not None: + req_param[str(key)] = val + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def get_file_list(self, + folder_path: Optional[str] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + pattern: Optional[str] = None, + filetype: Optional[str] = None, + goto_path: Optional[str] = None, + additional: Optional[str | list[str]] = None) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.List' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'additional']: + if val is not None: + req_param[str(key)] = val + + if folder_path is None: + return 'Enter a valid folder_path' + + if filetype is not None: + req_param['filetype'] = str(req_param['filetype']).lower() + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def get_file_info(self, + path: Optional[str] = None, + additional: Optional[str | list[str]] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.List' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = str(additional).replace("'", '"') + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + # TODO all working if specify extension check if correct [pattern, extension] + # it works if you put extension='...' + + def search_start(self, + folder_path: Optional[str] = None, + recursive: Optional[bool] = None, + pattern: Optional[str] = None, + extension: Optional[str] = None, + filetype: Optional[str] = None, + size_from: Optional[int] = None, + size_to: Optional[int] = None, + mtime_from: Optional[str | int] = None, + mtime_to: Optional[str | int] = None, + crtime_from: Optional[str | int] = None, + crtime_to: Optional[str | int] = None, + atime_from: Optional[str | int] = None, + atime_to: Optional[str | int] = None, + owner: Optional[str] = None, + group: Optional[str] = None + ) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.Search' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start', 'folder_path': ''} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param'] and 'time' not in key: + if val is not None: + req_param[str(key)] = val + if 'time' in key: + if val is not None: + try: + date = time.strptime(val, "%Y-%m-%d %H:%M:%S") + timestamp = time.mktime(date) + req_param[key] = '"' + str(timestamp) + '"' + except ValueError: + try: + datetime.fromtimestamp(int(val)).strftime('%Y-%m-%d %H:%M:%S') + req_param[key] = '"' + val + '"' + except ValueError: + return 'Enter the correct Date Time format "YYY-MM-DD HH:MM:SS" or Unix timestamp' + + if folder_path is None: + return 'Enter a valid folder_path' + else: + req_param['folder_path'] = '"' + folder_path + '"' + + if filetype is not None: + req_param['filetype'] = '"' + filetype + '"' + + response = self.request_data(api_name, api_path, req_param) + + taskid = response['data']['taskid'] + self._search_taskid = '"{}"'.format(taskid) + self._search_taskid_list.append('"' + response['data']['taskid'] + '"') + + message = ('You can now check the status of request with ' + 'get_search_list() , your id is: ' + self._search_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": taskid} + + return output + + def get_search_list(self, + task_id: str, + filetype: Optional[str] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + offset: Optional[int] = None, + additional: Optional[str | list[str]] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Search' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'taskid': ''} + + if task_id is None: + return 'Enter a correct taskid, choose one of the following: ' + str(self._search_taskid_list) + else: + req_param['taskid'] = task_id + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'additional', 'task_id']: + if val is not None: + req_param[str(key)] = val + + if filetype is not None: + req_param['filetype'] = str(filetype).lower() + + if additional is None: + additional = ['size', 'owner', 'time'] + + if type(additional) is list: + additional = '","'.join(additional) + + req_param['additional'] = '["' + additional + '"]' + + return self.request_data(api_name, api_path, req_param) + + def stop_search_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Search' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop', 'taskid': self._search_taskid} + + if taskid is None: + return 'Enter a valid taskid, choose between ' + str(self._search_taskid_list) + + self._search_taskid_list.remove(taskid) + + return self.request_data(api_name, api_path, req_param) + + def stop_all_search_task(self) -> str: + api_name = 'SYNO.FileStation.Search' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop', 'taskid': ''} + + assert len(self._search_taskid_list), 'Task list is empty' + str(self._search_taskid_list) + + for task_id in self._search_taskid_list: + req_param['taskid'] = task_id + self.request_data(api_name, api_path, req_param) + + self._search_taskid_list = [] + + return 'All task are stopped' + + def get_mount_point_list(self, + mount_type: Optional[str] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + additional: Optional[str | list[str]] = None + ) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.VirtualFolder' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + if mount_type is not None: + req_param['type'] = mount_type + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'additional', 'mount_type']: + if val is not None: + req_param[str(key)] = val + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def get_favorite_list(self, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + status_filter: Optional[str] = None, + additional: Optional[str | list[str]] = None + ) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'additional']: + if val is not None: + req_param[str(key)] = val + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def add_a_favorite(self, + path: str, + name: Optional[str] = None, + index: Optional[int] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'add'} + + if path is None: + return 'Enter a valid path' + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_a_favorite(self, path: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def clear_broken_favorite(self) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'clear_broken'} + + return self.request_data(api_name, api_path, req_param) + + def edit_favorite_name(self, path: str, new_name: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'edit'} + + if path is None: + return 'Enter a valid path' + else: + req_param['path'] = path + + if new_name is None: + return 'Enter a valid new_name' + else: + req_param['new_name'] = new_name + + return self.request_data(api_name, api_path, req_param) + + def replace_all_favorite(self, path: str | list[str], name: str | list[str]): + api_name = 'SYNO.FileStation.Favorite' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'edit'} + + if type(path) is list: + path = ','.join(path) + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + if type(name) is list: + name = ','.join(name) + req_param['name'] = name + elif name is not None: + req_param['name'] = name + else: + return 'Enter a valid name' + + return self.request_data(api_name, api_path, req_param) + + def start_dir_size_calc(self, path: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.DirSize' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + + response_id = '"{}"'.format(taskid) + self._dir_taskid = response_id + self._dir_taskid_list.append(response_id) + + message = ('You can now check the status of request ' + 'with get_dir_status() , your id is: ' + + response_id) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": taskid} + + return output + + def stop_dir_size_calc(self, taskid: str) -> str: + api_name = 'SYNO.FileStation.DirSize' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop', 'taskid': taskid} + + if taskid is None: + return 'Enter a valid taskid choose between: ' + str(self._dir_taskid_list) + else: + req_param['taskid'] = '"' + taskid + '"' + + self.request_data(api_name, api_path, req_param) + self._dir_taskid_list.remove('"' + taskid + '"') + + return 'The task has been stopped' + + def get_dir_status(self, taskid: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.DirSize' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status', 'taskid': taskid} + + if taskid is None and self._dir_taskid != '': + return 'Choose a taskid from this list: ' + str(self._dir_taskid) + + return self.request_data(api_name, api_path, req_param) + + def start_md5_calc(self, file_path: str) -> str | dict[str, object]: + api_name = 'SYNO.FileStation.MD5' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start'} + + if file_path is None: + return 'Enter a correct file_path' + else: + req_param['file_path'] = file_path + + self._md5_calc_taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + self._md5_calc_taskid_list.append(self._md5_calc_taskid) + + message = ('You can now check the status of request with ' + 'get_md5_status() , your id is: ' + self._md5_calc_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._md5_calc_taskid} + + return output + + def get_md5_status(self, taskid: Optional[str] = None) -> str | dict[str, object]: + api_name = 'SYNO.FileStation.MD5' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + if taskid is None and self._md5_calc_taskid != '': + req_param['taskid'] = '"{taskid}"'.format(taskid=self._md5_calc_taskid) + elif taskid is not None: + req_param['taskid'] = '"{taskid}"'.format(taskid=taskid) + else: + return 'Did you run start_md5_calc() first? No task id found! ' + str(self._md5_calc_taskid) + + return self.request_data(api_name, api_path, req_param) + + def stop_md5_calc(self, taskid: str) -> str: + api_name = 'SYNO.FileStation.DirSize' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop', 'taskid': taskid} + + if taskid is None: + return 'Enter a valid taskid choose between: ' + str(self._md5_calc_taskid_list) + else: + req_param['taskid'] = '"' + taskid + '"' + + self.request_data(api_name, api_path, req_param) + self._md5_calc_taskid_list.remove(taskid) + + return 'The task has been stopped' + + def check_permissions(self, + path: str, + filename: str, + overwrite: Optional[bool] = None, + create_only: Optional[bool] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.CheckPermission' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'write'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + if path is None: + return 'Enter a valid path' + + if filename is None: + return 'Enter a valid name' + + return self.request_data(api_name, api_path, req_param) + + def upload_file(self, + dest_path: str, + file_path: str, + create_parents: bool = True, + overwrite: bool = True, + verify: bool = False, + progress_bar: bool = True + ) -> str | tuple[int, dict[str, object]]: + api_name = 'SYNO.FileStation.Upload' + info = self.file_station_list[api_name] + api_path = info['path'] + filename = os.path.basename(file_path) + + session = requests.session() + + with open(file_path, 'rb') as payload: + url = ('%s%s' % (self.base_url, api_path)) + '?api=%s&version=%s&method=upload&_sid=%s' % ( + api_name, info['minVersion'], self._sid) + + encoder = MultipartEncoder({ + 'path': dest_path, + 'create_parents': str(create_parents).lower(), + 'overwrite': str(overwrite).lower(), + 'files': (filename, payload, 'application/octet-stream') + }) + + if progress_bar: + bar = tqdm.tqdm(desc='Upload Progress', + total=encoder.len, + dynamic_ncols=True, + unit='B', + unit_scale=True, + unit_divisor=1024 + ) + + monitor = MultipartEncoderMonitor(encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n)) + + r = session.post( + url, + data=monitor, + verify=verify, + headers={"X-SYNO-TOKEN": self.session._syno_token, 'Content-Type': monitor.content_type} + ) + + else: + r = session.post( + url, + data=encoder, + verify=verify, + headers={"X-SYNO-TOKEN": self.session._syno_token, 'Content-Type': encoder.content_type} + ) + + session.close() + if r.status_code != 200 or not r.json()['success']: + return r.status_code, r.json() + + return r.json() + + def get_shared_link_info(self, link_id: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'getinfo'} + + if link_id is None: + return 'Enter a valid id' + else: + req_param['id'] = link_id + + return self.request_data(api_name, api_path, req_param) + + def get_shared_link_list(self, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + force_clean: Optional[bool] = None + ) -> dict[str, object] | str: + + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_sharing_link(self, + path: str, + password: Optional[str] = None, + date_expired: Optional[str | int] = None, + date_available: Optional[str | int] = None, + expire_times: int = 0 + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + if path is None: + return 'Enter a valid path' + + return self.request_data(api_name, api_path, req_param) + + def delete_shared_link(self, link_id: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'delete'} + + if link_id is None: + return 'Enter a valid id' + else: + req_param['id'] = link_id + + return self.request_data(api_name, api_path, req_param) + + def clear_invalid_shared_link(self) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'clear_invalid'} + + return self.request_data(api_name, api_path, req_param) + + def edit_shared_link(self, + link_id: str, + password: Optional[str] = None, + date_expired: Optional[str | int] = None, + date_available: Optional[str | int] = None, + expire_times: int = 0 + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Sharing' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'edit'} + + if link_id is None: + return 'Enter a valid id' + else: + req_param['id'] = link_id + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_folder(self, + folder_path: str | list[str], + name: str | list[str], + force_parent: Optional[bool] = None, + additional: Optional[str | list[str]] = None + ) -> str | dict[str, object]: + api_name = 'SYNO.FileStation.CreateFolder' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'folder_path', 'additional', 'name']: + if val is not None: + req_param[str(key)] = val + + if type(folder_path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in folder_path] + folder_path = new_path + folder_path = '[' + ','.join(folder_path) + ']' + req_param['folder_path'] = folder_path + elif folder_path is not None: + req_param['folder_path'] = folder_path + else: + return 'Enter a valid path' + + if type(name) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in name] + name = new_path + name = '[' + ','.join(name) + ']' + req_param['name'] = name + elif name is not None: + req_param['name'] = '"' + name + '"' + else: + return 'Enter a valid path' + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def rename_folder(self, + path: str | list[str], + name: str | list[str], + additional: Optional[str | list[str]] = None, + search_taskid: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Rename' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'rename'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid folder path (folder path only ex. "/home/Drive/Downloads")' + + if type(name) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in name] + name = new_path + name = '[' + ','.join(name) + ']' + req_param['name'] = name + elif name is not None: + req_param['name'] = name + else: + return 'Enter a valid new folder name (new folder name only ex. "New Folder")' + + if additional is None: + additional = ['real_path', 'size', 'owner', 'time'] + + if type(additional) is list: + additional = ','.join(additional) + + req_param['additional'] = additional + + if search_taskid is not None: + req_param['search_taskid'] = search_taskid + + return self.request_data(api_name, api_path, req_param) + + def start_copy_move(self, + path: str | list[str], + dest_folder_path: str | list[str], + overwrite: Optional[bool] = None, + remove_src: Optional[bool] = None, + accurate_progress: Optional[bool] = None, + search_taskid: Optional[str] = None + ) -> str | dict[str, object]: + api_name = 'SYNO.FileStation.CopyMove' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + if type(dest_folder_path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in dest_folder_path] + dest_folder_path = new_path + dest_folder_path = '[' + ','.join(dest_folder_path) + ']' + req_param['name'] = dest_folder_path + elif dest_folder_path is not None: + req_param['dest_folder_path'] = dest_folder_path + else: + return 'Enter a valid path' + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'path', 'additional', + 'dest_folder_path', 'new_path']: + if val is not None: + req_param[str(key)] = val + + self._copy_move_taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + self._dir_taskid_list.append(self._copy_move_taskid) + + message = ('You can now check the status of request with ' + 'get_copy_move_status() , your id is: ' + + self._copy_move_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._copy_move_taskid} + + return output + + def get_copy_move_status(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.CopyMove' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + if taskid is None: + return 'Enter a valid taskid choose between ' + str(self._copy_move_taskid_list) + else: + req_param['taskid'] = '"' + taskid + '"' + + return self.request_data(api_name, api_path, req_param) + + def stop_copy_move_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.CopyMove' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop'} + + if taskid is None: + return 'Enter a valid taskid choose between ' + str(self._copy_move_taskid_list) + else: + req_param['taskid'] = taskid + + self._copy_move_taskid_list.remove(taskid) + + return self.request_data(api_name, api_path, req_param) + + def start_delete_task(self, + path: str | list[str], + accurate_progress: Optional[bool] = None, + recursive: Optional[bool] = None, + search_taskid: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Delete' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'path', 'new_path']: + if val is not None: + req_param[str(key)] = val + + self._delete_taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + self._delete_taskid_list.append(self._delete_taskid) + + message = ('You can now check the status of request with ' + 'get_delete_status() , task id is: ' + + self._delete_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._delete_taskid} + + return output + + def get_delete_status(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Delete' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + if taskid is None: + return 'Enter a valid taskid, choose between ' + str(self._delete_taskid_list) + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def stop_delete_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Delete' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop'} + + if taskid is None: + return 'Enter a valid taskid, choose between ' + str(self._delete_taskid_list) + else: + req_param['taskid'] = taskid + + self._delete_taskid_list.remove('"' + taskid + '"') + + return self.request_data(api_name, api_path, req_param) + + def delete_blocking_function(self, + path: str, + recursive: Optional[bool] = None, + search_taskid: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Delete' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'delete'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param', 'path', 'new_path']: + if val is not None: + req_param[str(key)] = val + + 'This function will stop your script until done! Do not interrupt ' + + return self.request_data(api_name, api_path, req_param) + + def start_extract_task(self, + file_path: str, + dest_folder_path: str, + overwrite: Optional[bool] = None, + keep_dir: Optional[bool] = None, + create_subfolder: Optional[bool] = None, + codepage: Optional[str] = None, + password: Optional[str] = None, + item_id: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Extract' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start', 'file_path': file_path, + 'dest_folder_path': dest_folder_path} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + if file_path is None: + return 'Enter a valid file_path' + + if dest_folder_path is None: + return 'Enter a valid dest_folder_path' + + self._extract_taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + self._extract_taskid_list.append(self._extract_taskid) + + message = ('You can now check the status of request with ' + 'get_extract_status() , your id is: ' + + self._extract_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._extract_taskid} + + return output + + def get_extract_status(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Extract' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + if taskid is None: + return 'Enter a valid taskid, choose between ' + str(self._extract_taskid_list) + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def stop_extract_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Extract' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop'} + + if taskid is None: + return 'Enter a valid taskid, choose between ' + str(self._extract_taskid_list) + else: + req_param['taskid'] = taskid + + self._extract_taskid_list.remove(taskid) + + return self.request_data(api_name, api_path, req_param) + + def get_file_list_of_archive(self, + file_path: str, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + codepage: Optional[str] = None, + password: Optional[str] = None, + item_id: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Extract' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + if file_path is None: + return 'Enter a valid file_path' + + return self.request_data(api_name, api_path, req_param) + + def start_file_compression(self, + path: str | list[str], + dest_file_path: str, + level: Optional[int] = None, + mode: Optional[str] = None, + compress_format: Optional[str] = None, + password: Optional[str] = None + ) -> dict[str, object] | str | tuple[str]: + api_name = 'SYNO.FileStation.Compress' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'start'} + + if type(path) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in path] + path = new_path + path = '[' + ','.join(path) + ']' + req_param['path'] = path + elif path is not None: + req_param['path'] = path + else: + return 'Enter a valid path' + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'compress_format', '_password', '_api_path', + 'req_param', 'path', 'new_path']: + if val is not None: + req_param[str(key)] = val + + if dest_file_path is None: + return 'Enter a valid dest_file_path' + + if compress_format is not None: + req_param['format'] = compress_format + + if password is not None: + req_param['_password'] = password + + self._compress_taskid = self.request_data(api_name, api_path, req_param)['data']['taskid'] + + message = ('You can now check the status of request with ' + 'get_compress_status() , your id is: ' + + self._compress_taskid) + if self.interactive_output: + output = message + else: + output = {"message": message, "taskid": self._compress_taskid} + + return output + + def get_compress_status(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Compress' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status'} + + if taskid is None: + return 'Enter a valid taskid' + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def stop_compress_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.Compress' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'stop'} + + if taskid is None: + return 'Enter a valid taskid' + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def get_list_of_all_background_task(self, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort_by: Optional[str] = None, + sort_direction: Optional[str] = None, + api_filter: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.FileStation.BackgroundTask' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + if type(api_filter) is list: + new_path = [] + [new_path.append('"' + x + '"') for x in api_filter] + api_filter = new_path + api_filter = '[' + ','.join(api_filter) + ']' + req_param['api_filter'] = api_filter + + return self.request_data(api_name, api_path, req_param) + + def get_file(self, + path: str, + mode: str, + dest_path: str = ".", + chunk_size: int = 8192, + verify: bool = False + ) -> Optional[str]: + + api_name = 'SYNO.FileStation.Download' + info = self.file_station_list[api_name] + api_path = info['path'] + + if path is None: + return 'Enter a valid path' + + session = requests.session() + + url = ('%s%s' % (self.base_url, api_path)) + '?api=%s&version=%s&method=download&path=%s&mode=%s&_sid=%s' % ( + api_name, info['maxVersion'], parse.quote_plus(path), mode, self._sid) + + if mode is None: + return 'Enter a valid mode (open / download)' + + if mode == r'open': + with session.get(url, stream=True, verify=verify, headers={"X-SYNO-TOKEN": self.session._syno_token}) as r: + r.raise_for_status() + for chunk in r.iter_content(chunk_size=chunk_size): + if chunk: # filter out keep-alive new chunks + sys.stdout.buffer.write(chunk) + + if mode == r'download': + with session.get(url, stream=True, verify=verify, headers={"X-SYNO-TOKEN": self.session._syno_token}) as r: + r.raise_for_status() + if not os.path.isdir(dest_path): + os.makedirs(dest_path) + with open(dest_path + "/" + os.path.basename(path), 'wb') as f: + for chunk in r.iter_content(chunk_size=chunk_size): + if chunk: # filter out keep-alive new chunks + f.write(chunk) + + if mode == r'serve': + with session.get(url, stream=True, verify=verify, headers={"X-SYNO-TOKEN": self.session._syno_token}) as r: + r.raise_for_status() + return io.BytesIO(r.content) + +# TODO SYNO.FileStation.Thumb to be done diff --git a/synology_api/log_center.py b/synology_api/log_center.py new file mode 100644 index 0000000..3f508b0 --- /dev/null +++ b/synology_api/log_center.py @@ -0,0 +1,70 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class LogCenter(base_api.BaseApi): + + def logcenter(self) -> dict[str, object] | str: + api_name = 'SYNO.LogCenter.RecvRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def client_status_cnt(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.Status' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'cnt_get'} + + return self.request_data(api_name, api_path, req_param) + + def client_status_eps(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.Status' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'eps_get'} + + return self.request_data(api_name, api_path, req_param) + + def remote_log_archives(self) -> dict[str, object] | str: + api_name = 'SYNO.LogCenter.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_remotearch_subfolder'} + + return self.request_data(api_name, api_path, req_param) + + def display_logs(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SyslogClient.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def setting_storage_list(self) -> dict[str, object] | str: + api_name = 'SYNO.LogCenter.Setting.Storage' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def registry_send_list(self) -> dict[str, object] | str: + api_name = 'SYNO.LogCenter.Client' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def history(self) -> dict[str, object] | str: + api_name = 'SYNO.LogCenter.History' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/notestation.py b/synology_api/notestation.py new file mode 100644 index 0000000..b873f46 --- /dev/null +++ b/synology_api/notestation.py @@ -0,0 +1,100 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class NoteStation(base_api.BaseApi): + + def settings_info(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Setting' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + # TODO success response but need more info about it + '''def notestation_settings_init(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Setting' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'init'} + + return self.request_data(api_name, api_path, req_param)''' + + def info(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Info' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def notebooks_info(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Notebook' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def tags_info(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Tag' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def shortcuts(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Shortcut' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def todo(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Todo' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def smart(self) -> dict[str, object] | str: # TODO need to investigate for additional params + api_name = 'SYNO.NoteStation.Smart' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def note_list(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Note' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def specific_note_id(self, note_id) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Note' + info = self.gen_list[api_name] + api_path = info['path'] + + if note_id is None: + return 'note_id must be specify, run note_list() and copy object_id that you need' + + req_param = {'version': info['maxVersion'], 'method': 'get', 'object_id': note_id} + + return self.request_data(api_name, api_path, req_param) + + # TODO success response but need additional data + '''def note_idle(self) -> dict[str, object] | str: + api_name = 'SYNO.NoteStation.Note' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'idle'} + + return self.request_data(api_name, api_path, req_param)''' diff --git a/synology_api/oauth.py b/synology_api/oauth.py new file mode 100644 index 0000000..8a98261 --- /dev/null +++ b/synology_api/oauth.py @@ -0,0 +1,31 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class OAuth(base_api.BaseApi): + + def clients(self, offset: int = 0, limit: int = 20) -> dict[str, object] | str: + api_name = 'SYNO.OAUTH.Client' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def tokens(self, offset: int = 0, limit: int = 20) -> dict[str, object] | str: + api_name = 'SYNO.OAUTH.Token' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def logs(self, offset: int = 0, limit: int = 20) -> dict[str, object] | str: + api_name = 'SYNO.OAUTH.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'action': 'list', + 'offset': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/photos.py b/synology_api/photos.py new file mode 100644 index 0000000..2d7f99d --- /dev/null +++ b/synology_api/photos.py @@ -0,0 +1,287 @@ +from __future__ import annotations +from typing import Optional, Any +from . import base_api +import json + + +class Photos(base_api.BaseApi): + + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None + ) -> None: + + super(Photos, self).__init__(ip_address, port, username, password, secure, cert_verify, + dsm_version, debug, otp_code, 'FotoStation') + + self.session.get_api_list('Foto') + + self.request_data: Any = self.session.request_data + self.photos_list: Any = self.session.app_api_list + self.base_url: str = self.session.base_url + + self._userinfo: Any = None + + def get_userinfo(self) -> Any: + if self._userinfo is not None: + return self._userinfo + + api_name = 'SYNO.Foto.UserInfo' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'me'} + self._userinfo = self.request_data(api_name, api_path, req_param) + + return self._userinfo + + def get_folder(self, folder_id: int = 0) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Browse.Folder' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'id': folder_id} + + return self.request_data(api_name, api_path, req_param) + + def list_folders(self, + folder_id: int = 0, + limit: int = 1000, + offset: int = 0, + additional: str | list[str] = None + ) -> dict[str, object] | str: + return self._list_folders(folder_id, limit, offset, additional, 'SYNO.Foto.Browse.Folder') + + def list_teams_folders(self, + folder_id: int = 0, + limit: int = 2000, + offset: int = 0, + additional: Optional[str | list[str]] = None + ) -> dict[str, object] | str: + return self._list_folders(folder_id, limit, offset, additional, 'SYNO.FotoTeam.Browse.Folder') + + def _list_folders(self, folder_id: int, limit: int, offset: int, additional: Optional[str | list[str]], + api_name: str) -> Any: + if additional is None: + additional = [] + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'id': folder_id, 'limit': limit, 'offset': offset, + 'additional': json.dumps(additional)} + + return self.request_data(api_name, api_path, req_param) + + def count_folders(self, folder_id: int = 0) -> dict[str, object] | str: + return self._count_folders(folder_id, 'SYNO.Foto.Browse.Folder') + + def count_team_folders(self, folder_id: int = 0) -> dict[str, object] | str: + return self._count_folders(folder_id, 'SYNO.FotoTeam.Browse.Folder') + + def _count_folders(self, folder_id: int, api_name: str) -> Any: + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'count', 'id': folder_id} + + return self.request_data(api_name, api_path, req_param) + + def lookup_folder(self, path: str) -> dict[str, object] | str: + return self._lookup_folder(path, 'SYNO.FotoBrowse.Folder', 'SYNO.Foto.Browse.Folder') + + def lookup_team_folder(self, path: str) -> dict[str, object] | str: + return self._lookup_folder(path, 'SYNO.FotoTeam.Browse.Folder', 'SYNO.FotoTeam.Browse.Folder') + + def _lookup_folder(self, path: str, api_name_count: str, api_name_list: str) -> Optional[dict[str, object]]: + parent = 0 + found_path = '' + folder = '' + for part in path.strip('/').split('/'): + count_response = self._count_folders(parent, api_name_count) + if not count_response['success']: + return + count = count_response['data']['count'] + for offset in range(0, count, 1000): + folders_response = self._list_folders(parent, limit=1000, offset=offset, additional=None, + api_name=api_name_list) + if not folders_response['success']: + return + folder = next(filter(lambda elem: elem['name'] == '%s/%s' % (found_path, part), + folders_response['data']['list']), None) + if folder: + parent = folder['id'] + found_path = folder['name'] + break + else: + return + return folder + + def get_album(self, album_id: str, additional: Optional[str | list[str]] = None) -> dict[str, object] | str: + if not isinstance(album_id, list): + album_id = [album_id] + if additional is None: + additional = [] + api_name = 'SYNO.Foto.Browse.Album' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'id': json.dumps(album_id), + 'additional': json.dumps(additional)} + + return self.request_data(api_name, api_path, req_param) + + def list_albums(self, offset: int = 0, limit: int = 100) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Browse.Album' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def suggest_condition(self, + keyword: str, + condition: Optional[list[str]] = None, + user_id: Optional[str] = None + ) -> dict[str, object] | str: + if condition is None: + condition = ['general_tag'] + if user_id is None: + user_id = self.get_userinfo()['data']['id'] + + api_name = 'SYNO.Foto.Browse.ConditionAlbum' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'suggest', 'user_id': user_id, 'keyword': keyword, + 'condition': json.dumps(condition)} + + return self.request_data(api_name, api_path, req_param) + + def create_album(self, name: str, condition: list[str]) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Browse.ConditionAlbum' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create', 'name': name, + 'condition': json.dumps(condition)} + + return self.request_data(api_name, api_path, req_param) + + def delete_album(self, album_id: str) -> dict[str, object] | str: + if not isinstance(album_id, list): + album_id = [album_id] + api_name = 'SYNO.Foto.Browse.Album' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'delete', 'id': json.dumps(album_id)} + + return self.request_data(api_name, api_path, req_param) + + def set_album_condition(self, folder_id: int, condition: list[str]) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Browse.ConditionAlbum' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set_condition', 'id': folder_id, + 'condition': json.dumps(condition)} + + return self.request_data(api_name, api_path, req_param) + + def share_album(self, + album_id: str, + permission: Optional[str | list[str]] = None, + enabled: bool = True, + expiration: int | str = 0 + ) -> Any: + self._share('SYNO.Foto.Sharing.Passphrase', policy='album', permission=permission, album_id=album_id, + enabled=enabled, expiration=expiration) + + def share_team_folder(self, + folder_id: int, + permission: Optional[str] = None, + enabled: bool = True, + expiration: int | str = 0 + ) -> Any: + self._share('SYNO.FotoTeam.Sharing.Passphrase', policy='folder', permission=permission, folder_id=folder_id, + enabled=enabled, expiration=expiration) + + def _share(self, + api_name: str, + policy: str, + permission: str, + expiration: int | str, + **kwargs + ) -> dict[str, object] | Any: + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set_shared', 'policy': policy, **kwargs} + + shared_response = self.request_data(api_name, api_path, req_param) + if not shared_response['success']: + return + + if not permission: + return shared_response + + passphrase = shared_response['data']['passphrase'] + + req_param = {'version': info['maxVersion'], 'method': 'update', 'passphrase': passphrase, + 'expiration': expiration, 'permission': json.dumps(permission)} + return self.request_data(api_name, api_path, req_param) + + def list_shareable_users_and_groups(self, team_space_sharable_list: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Sharing.Misc' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list_user_group', + 'team_space_sharable_list': team_space_sharable_list} + + return self.request_data(api_name, api_path, req_param) + + def list_item_in_folders(self, offset: int = 0, limit: int = 0, folder_id: int = 0, sort_by: str = 'filename', + sort_direction: str = 'desc', type: str = None, passphrase: str = None, + additional: list = None) -> dict[str, object] | str: + + """List all items in all folders in Personal Space + + Parameters + ---------- + offset: int Required. Specify how many shared folders are skipped before beginning to return listed shared folders + limit: int Required. Number of shared folders requested. 0 lists all shared folders. + folder_id: int ID of folder + sort_by: str Optional filename, filesize, takentime, item_type + sort_direction: str Optional asc or desc + passphrase: str Optional Passphrase for a shared album + additional: list ["thumbnail","resolution", "orientation", "video_convert", "video_meta", "provider_user_id", "exif", "tag", "description", "gps", "geocoding_id", "address", "person"] + type: str 'Type of data photo: Photo video: Video live: iPhone live photos' + """ + + api_name = 'SYNO.Foto.Browse.Item' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offset': offset, 'limit': limit, + 'folder_id': folder_id, 'sort_by': sort_by, 'sort_direction': sort_direction} + + if type: + req_param['type'] = type + if passphrase: + req_param['passphrase']: passphrase + if additional: + req_param['additional']: additional + + return self.request_data(api_name, api_path, req_param) + + def list_search_filters(self) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Search.Filter' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def get_guest_settings(self) -> dict[str, object] | str: + api_name = 'SYNO.Foto.Setting.Guest' + info = self.photos_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/qnap.py b/synology_api/qnap.py new file mode 100644 index 0000000..d3082cc --- /dev/null +++ b/synology_api/qnap.py @@ -0,0 +1,7 @@ +from synology_api import filestation, downloadstation +ip='192.168.88.10' +port='8080' +username='admin' +password='cbvgcjy0' +fl = filestation.FileStation(ip, port, username, password, secure=True, cert_verify=True, dsm_version=7, debug=True, otp_code=None) +fl.get_info() \ No newline at end of file diff --git a/synology_api/security_advisor.py b/synology_api/security_advisor.py new file mode 100644 index 0000000..c957a01 --- /dev/null +++ b/synology_api/security_advisor.py @@ -0,0 +1,54 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class SecurityAdvisor(base_api.BaseApi): + + def general_info(self) -> dict[str, object] | str: + api_name = 'SYNO.SecurityAdvisor.Conf.Location' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def security_scan(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SecurityScan.Conf' + info = self.core_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def checklist(self) -> dict[str, object] | str: + api_name = 'SYNO.SecurityAdvisor.Conf.Checklist' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'group': 'home'} + + return self.request_data(api_name, api_path, req_param) + + def login_activity(self, offset: int = 0, limit: int = 20) -> dict[str, object] | str: + api_name = 'SYNO.SecurityAdvisor.LoginActivity' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'offser': offset, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def advisor_config(self) -> dict[str, object] | str: + api_name = 'SYNO.SecurityAdvisor.Conf' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def scan_config(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.SecurityScan.Conf' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'group_enum', 'argGroup': 'custom'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/snapshot.py b/synology_api/snapshot.py new file mode 100644 index 0000000..4a1c5b5 --- /dev/null +++ b/synology_api/snapshot.py @@ -0,0 +1,261 @@ +from __future__ import annotations +from typing import Optional +from . import base_api +import json + + +class Snapshot(base_api.BaseApi): + """Class for interacting with Snapshot APIs. + + This class implements APIs to manage snapshots. + There is no documentation for these APIs, so the implementation is based on network inspection. + + Examples + -------- + List snapshots for a share: + >>> from synology_api import snapshot + >>> ss = snapshot.Snapshot('IP', 'PORT', 'USER', 'PASSWORD') + >>> resp = ss.list_snapshots('share_name') + >>> print(resp) + + Create a snapshot for a share: + >>> resp = ss.create_snapshot('share_name') + >>> print(resp) + + Delete snapshots for a share: + >>> resp = ss.delete_snapshots('share_name', ['snapshot_name']) + >>> print(resp) + + Set attributes for a snapshot: + >>> resp = ss.set_snapshot_attr('share_name', 'snapshot_name', description='new description', lock=True) + >>> print(resp) + """ + + def list_snapshots(self, + share_name: str, + attribute_filter: list[str] = [], + additional_attribute: list[str] = [], + offset: int = 0, + limit: int = -1) -> dict[str, object] | str: + """List snapshots for a share. + + Parameters + ---------- + share_name : str + Name of the share to list snapshots for + attribute_filter : list[str], optional + List of attributes filter to apply. Defaults to [] (no filter). + + Each attribute filter is a string in the format of "attr==value" or "attr=value" and + optionally prefixed with "!" to negate the filter. + The following are examples of valid attribute filters: + + - ["!hide==true", "desc==abc"] # hide is not true and desc is exactly abc + - ["desc=abc"] # desc has abc in it + additional_attribute : list[str], optional + List of snapshot attributes whose values are included in the response. + Defaults to [] (only time is returned). + + Note that not all attributes are available via API. The following are confirmed to work: + + - desc + - lock + - worm_lock + - schedule_snapshot + offset : int, optional + Offset to start listing from. Defaults to 0. + limit : int, optional + Number of snapshots to return. Defaults to -1 (all). + + Returns + ------- + dict[str, object] | str + API response if successful, error message if not + + Example: + { + "data": { + "snapshots": [ + { + "desc": "", + "lock": true, + "schedule_snapshot": false, + "time": "GMT+09-2023.09.11-23.23.40", + "worm_lock": true, + "worm_lock_begin": "1694442321", + "worm_lock_day": "1", + "worm_lock_end": "1694528721" + } + ], + "total": 1 + }, + "success": true + } + """ + + api_name = 'SYNO.Core.Share.Snapshot' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '2', + 'method': 'list', + 'name': share_name, + 'filter': json.dumps({"attr": attribute_filter}), + 'additional': json.dumps(additional_attribute), + 'offset': offset, + 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def create_snapshot(self, + share_name: str, + description: str = "", + lock: bool = False, + immutable: bool = False, + immutable_days: int = 7, + ) -> dict[str, object] | str: + """Create a snapshot for a share. + + Parameters + ---------- + share_name : str + Name of the share to create a snapshot for + description : str, optional + Description of the snapshot. Defaults to "". + lock : bool, optional + Whether to lock the snapshot. Defaults to False. + immutable : bool, optional + Whether to make the snapshot immutable. Defaults to False. + immutable_days : int, optional + Number of days to make the snapshot immutable for. Defaults to 7. + Must be greater than 0. Mandatory if immutable is True. + + Returns + ------- + dict[str, object] | str + API response if successful, error message if not + + Example: + { + "data": "GMT+09-2023.09.12-00.33.20", + "success": true + } + """ + + api_name = 'SYNO.Core.Share.Snapshot' + info = self.gen_list[api_name] + api_path = info['path'] + + snapinfo = { + "desc": description, + "lock": lock, + } + if immutable == True: + snapinfo['worm_lock'] = True + if immutable_days < 1: + return "immutable_days must be greater than 0" + snapinfo['worm_lock_day'] = immutable_days + req_param = {'version': '1', + 'method': 'create', + 'snapinfo': json.dumps(snapinfo), + 'name': share_name} + + return self.request_data(api_name, api_path, req_param) + + def delete_snapshots(self, + share_name: str, + snapshots: list[str] + ) -> dict[str, object] | str: + """Delete snapshots for a share. + + Parameters + ---------- + share_name : str + Name of the share to delete snapshots for + snapshots : list[str] + List of snapshots to delete + + Returns + ------- + dict[str, object] | str + API response if successful, error message if not + + Example: + { + "success": true + } + """ + + api_name = 'SYNO.Core.Share.Snapshot' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = {'version': '1', + 'method': 'delete', + 'name': share_name, + 'snapshots': json.dumps(snapshots)} + + return self.request_data(api_name, api_path, req_param) + + def set_snapshot_attr(self, + share_name: str, + snapshot: str, + description: Optional[str] = None, + lock: Optional[bool] = None, + immutable: Optional[bool] = None, + immutable_days: Optional[int] = None + ) -> dict[str, object] | str: + """Set attributes for a snapshot. + + Parameters + ---------- + share_name : str + Name of the share to set attributes for + snapshot : str + Name of the snapshot to set attributes for + description : str, optional + Description of the snapshot. Defaults to None (no change). + lock : bool, optional + Whether to lock the snapshot. Defaults to None (no change). + immutable : bool, optional + Whether to make the snapshot immutable. Defaults to None (no change). + immutable_days : int, optional + Number of days to make the snapshot immutable for. Defaults to None (no change). + Must be greater than 0. Mandatory if immutable is True. + + Returns + ------- + dict[str, object] | str + API response if successful, error message if not + + Example: + { + "success": true + } + """ + + api_name = 'SYNO.Core.Share.Snapshot' + info = self.gen_list[api_name] + api_path = info['path'] + + snapinfo = {} + if description is not None: + snapinfo['desc'] = description + if lock is not None: + snapinfo['lock'] = lock + if immutable is not None: + if immutable == False: + return "immutable cannot be set to False" + if immutable_days is None: + return "immutable_days must be specified if immutable is True" + if immutable_days < 1: + return "immutable_days must be greater than 0" + snapinfo['worm_lock'] = True + snapinfo['worm_lock_day'] = immutable_days + req_param = {'version': '1', + 'method': 'set', + 'snapinfo': json.dumps(snapinfo), + 'name': share_name, + 'snapshot': snapshot} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/surveillancestation.py b/synology_api/surveillancestation.py new file mode 100644 index 0000000..d5d8b65 --- /dev/null +++ b/synology_api/surveillancestation.py @@ -0,0 +1,5267 @@ +from __future__ import annotations +from typing import Optional, Any +from . import base_api + + +class SurveillanceStation(base_api.BaseApi): + + def surveillance_station_info(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Info' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + return self.request_data(api_name, api_path, req_param) + + def camera_save(self, id: str = None, + name: str = None, + dsld: int = None, + newName: str = None, + ip: str = None, + port: int = None, + vendor: str = None, + model: str = None, + userName: str = None, + password: str = None, + videoCodec: int = None, + audioCodec: int = None, + tvStandard: int = None, + channel: str = None, + userDefinePath: str = None, + fov: str = None, + streamXX: Any = None, + recordTime: int = None, + preRecordTime: int = None, + postRecordTime: int = None, + enableRecordingKeepDays: bool = None, + recordingKeepDays: int = None, + enableRecordingKeepSize: bool = None, + recordingKeepSize: int = None, + enableLowProfile: bool = None, + recordSchedule: list[int] = None, + rtspPathTimeout: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def camera_list(self, idList: str = None, + offset: int = None, + limit: int = None, + blFromCamList: bool = None, + blIncludeDeletedCam: bool = None, + privCamType: str = None, + basic: bool = None, + streamInfo: bool = None, + blPrivilege: bool = None, + camStm: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_camera_info(self, + cameraIds: int = None, + privCamType: int = 1, + blIncludeDeletedCam: bool = True, + basic: bool = True, + streamInfo: bool = True, + optimize: bool = True, + ptz: bool = True, + eventDetection: bool = True, + deviceOutCap: bool = True, + fisheye: bool = True, + camAppInfo: bool = True) -> dict[str, object] | str: + ''' + This function return information about a camera. + + cameraIds : This parameter is named cameraIds in the API documentation but it refer to 1 camera ID + + privCamType: int = 1 + SYNO.SS.CamPriv.LIVEVIEW = 1; + SYNO.SS.CamPriv.PLAYBACK = 2; + SYNO.SS.CamPriv.LENS = 4; + SYNO.SS.CamPriv.AUDIO = 8; + SYNO.SS.CamPriv.DIGIOUT = 16; + + All other parameters must be let to default value + ''' + + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['minVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def camera_list_group(self, + offset: int = None, + limit: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_snapshot(self, + id: Any = None, + name: str = None, + dsld: int = None, + profileType: int = 1) -> str: + ''' By default, the profileType is 1, which is the default profile. + Binary data is returned, so the response is not a json object. + ''' + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSnapshot'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + ## Make sure to disable json response, as the response is a binary file + ## Return only the content of the response where binary data is stored + return self.request_data(api_name, api_path, req_param, response_json=False).content + + def enable_camera(self, + idList: str = None, + blIncludeDeletedCam: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_camera(self, + idList: str = None, + blIncludeDeletedCam: bool = False) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_capability_by_cam_id(self, cameraId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCapabilityByCamId', 'cameraId': cameraId} + + return self.request_data(api_name, api_path, req_param) + + def count_occupied_size(self, camId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetOccupiedSize', 'camId': camId} + + return self.request_data(api_name, api_path, req_param) + + def is_shortcut_valid(self, cameraId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckCamValid', 'cameraId': cameraId} + + return self.request_data(api_name, api_path, req_param) + + def get_live_path(self, idList: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetLiveViewPath', 'idList': idList} + + return self.request_data(api_name, api_path, req_param) + + def audio_event_enum(self, camId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AudioEnum', 'camId': camId} + + return self.request_data(api_name, api_path, req_param) + + def alarm_event_enum(self, camId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AlarmEnum', 'camId': camId} + + return self.request_data(api_name, api_path, req_param) + + def md_parameter_save(self, camId: int = None, + source: int = None, + mode: int = None, + sensitivity: int = None, + threshold: int = None, + objectSize: int = None, + percentage: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'MDParamSave'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def motion_event_enum(self, camId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'MotionEnum', 'camId': camId} + + return self.request_data(api_name, api_path, req_param) + + def motion_parameter_save(self, + camId: int = None, + source: int = None, + mode: int = None, + keep: bool = None, + level: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ADParamSave'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def di_parameter_save(self, + camId: int = None, + idx: int = None, + keep: bool = None, + normal: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DIParamSave'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def alarm_sts_polling(self, + camId: int = None, + timeOut: int = None, + keep: Any = None) -> dict[str, object] | str: # TODO not working + + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AlarmStsPolling'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def td_parameter_save(self, + camId: int = None, + source: int = None, + keep: Any = None, + duration: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera.Event' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'TDParamSave'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_camera_group(self, privCamType: int = None) -> dict[str, object] | str: + + api_name = 'SYNO.SurveillanceStation.Camera.Group' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum', 'privCamType': privCamType} + + return self.request_data(api_name, api_path, req_param) + + def save_specific_group(self, groupList: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Camera.Group' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save', 'groupList': groupList} + + return self.request_data(api_name, api_path, req_param) + + def delete_specific_groups(self, Id: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Group' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete', 'id': Id} + + return self.request_data(api_name, api_path, req_param) + + def enumerate_group_information(self, camServerId: int = None, + shareName: str = None, + archiveName: str = None, + camlist: Any = None, + actFromHost: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Import' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_camera_from_archive(self, + shareName: str = None, + archiveName: str = None, + serverId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Import' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ArchiveCamEnum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_archive_from_folder(self, + shareName: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Import' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ArchiveEnum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_available_size_of_sdcard(self, + camId: Any = None, + host: str = None, + port: str = None, + user: str = None, + passw: str = None, + vendor: str = None, + model: str = None, + ch: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Wizard' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ArchiveEnum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key is passw: + req_param[str('pass')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_licence_quota(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Wizard' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckQuota'} + + return self.request_data(api_name, api_path, req_param) + + def format_specific_sd_card(self, + camId: Any = None, + host: str = None, + port: str = None, + user: str = None, + passw: str = None, + vendor: str = None, + model: str = None, + ch: str = None, + timeout: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Wizard' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'FormatSDCard'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key is passw: + req_param[str('pass')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def quick_create_single_camera(self, + camServerId: Any = None, + actFromHost: bool = None, + camStreamingType: str = None, + camName: str = None, + camIP: str = None, + camPort: str = None, + camVendor: str = None, + camModel: str = None, + camMountType: int = None, + camChannel: str = None, + camVideoType: str = None, + camAudioType: str = None, + camSourcePath: str = None, + camUserName: str = None, + camPassWord: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Camera.Wizard' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'QuickCreate'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def move_camera_lens(self, + cameraId: Any = None, + direction: str = None, + speed: int = None, + moveType: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Move'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def camera_lens_zoom(self, + cameraId: Any = None, + control: Any = None, + moveType: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Zoom'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_preset_ptz_camera(self, + cameraId: Any = None, + offset: int = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def move_camera_lens_to_preset_position(self, + cameraId: Any = None, + presetId: Any = None, + position: Any = None, + speed: Any = None, + type: Any = None, + isPatrol: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GoPreset'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_patrol_cameras(self, + cameraId: Any = None, + offset: int = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListPatrol'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def force_cam_to_execute_patrol(self, + cameraId: Any = None, + patrolId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RunPatrol'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def focus_camera(self, + cameraId: Any = None, + control: Any = None, + moveType: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Focus'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def control_camera_iris_in_out(self, + cameraId: Any = None, + control: Any = None, + moveType: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Iris'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def auto_focus(self, cameraId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AutoFocus'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def move_cam_lens_to_absolute_position(self, + posX: int = None, + posY: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AbsPtz'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def move_cam_to_home_position(self, + cameraId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Home'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def auto_pan_camera(self, + cameraId: Any = None, + moveType: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AutoPan'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_stop_object_tracking(self, + cameraId: Any = None, + moveType: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ObjTracking'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_stop_external_recording(self, + cameraId: Any = None, + action: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ExternalRecording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Record'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def query_event_list_by_filter(self, + offset: int = None, + limit: int = None, + cameraIds: str = None, + fromTime: int = None, + toTime: int = None, + dsld: int = None, + mountId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_recordings(self, + idList: int = None, + dsld: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_events_by_filter(self, + reason: str = None, + cameraIds: str = None, + fromTime: Any = None, + toTime: Any = None, + locked: int = None, + evtSrcType: int = None, + evtSrcId: int = None, + blIncludeSnapshot: bool = None, + includeAllCam: bool = None, + from_end: int = None, + from_start: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteFilter'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_all_recordings(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteAll'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def apply_settings_advance_tab(self, + rotateUnrecogCam: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ApplyAdvanced'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def count_by_number_of_event(self, + offset: bool = None, + limit: int = None, + reason: str = None, + cameraIds: str = None, + fromTime: int = None, + toTime: int = None, + locked: int = None, + evtSrcType: int = None, + evtSrcId: int = None, + blIncludeSnapshot: bool = None, + includeAllCam: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategory'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def keep_event_play_alive(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Keepalive'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def stop_recording_event(self, + idList: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Trunc'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def load_settings_in_advanced_tab(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'LoadAdvanced'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_selected_event(self, + reason: str = None, + cameraIds: str = None, + fromTime: int = None, + toTime: int = None, + locked: int = None, + evtSrcType: int = None, + evtSrcId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'LockFilter'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_selected_event(self, + idList: str = None, + dsld: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_selected_filter_event(self, + reason: str = None, + cameraIds: str = None, + fromTime: int = None, + toTime: int = None, + locked: int = None, + evtSrcType: int = None, + evtSrcId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'UnlockFilter'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_selected_recordings(self, + idList: str = None, + dsld: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def download_recordings(self, + id: int = None, + mountId: int = None, + offsetTimeMs: int = None, + playTimeMs: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Download'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param, response_json=False) + + def check_if_recording_playable(self, + eventId: int = None, + chkDetail: bool = None, + mountId: int = None, + dsld: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckEventValid'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def play_specific_recording(self, + recordingId: int = None, + alertRecording: bool = None, + mountId: int = None, + dsld: int = None, + videoCodec: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Stream'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def download_merged_recording_files(self, + camId: int = None, + fromTime: int = None, + toTime: int = None, + fileName: str = None) -> dict[str, object] | str: # TODO not working + + """Download the merged files of UTC time range recordings of target camera. + If there are different resolution or codec within UTC time range, the recordings will merge as much as possible + and downlod file will be a zip file. + + This method will start a task which have keep-alive mechanism. + Use GetRangeExportProgress method to get newest progress and keep-alive. + After receiving progress 100, use OnRangeExportDone method to download exported recording within 1 + minutes. + If you want to cancel range export task, just do not send GetRangeExportProgress method or + OnRangeExportDone method. System will cleanup processed files itself.""" + + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RangeExport'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_newest_progress_keep_alive(self, dlid: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetRangeExportProgress'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def download_recording_from_target(self, + dlid: int = None, + fileName: str = None) -> dict[str, object] | str: # TODO not working + + """Response + MP4 or zip file data. + The response type can be found in fileExt of GetRangeExportProgress method response when progress 100. + + Note + GetRangeExportProgress method must be sent within 1 minute after corresponding RangeExport method task + is completed, otherwise the exported recordings will be cleared. + + 2.3.11.20 API Error Code + Code Description + 400 Execution failed. + 401 Parameter invalid. + 405 CMS server connection failed. + 414 Some events not exist. + 439 Too many items selected.""" + + api_name = 'SYNO.SurveillanceStation.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'OnRangeExportDone'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def handle_load_event_export(self, + start: int = None, + limit: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_name_export_event(self, + dsId: int = None, + name: int = None, + share: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckName'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_camera_information_list(self, + dslld: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CamEnum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_destination_folder_availability(self, + freeSize: int = None, + startTime: int = None, + stopTime: int = None, + camIdList: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckAvailableExport'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def handle_save_event_export(self, + name: str = None, + srcDsId: int = None, + dstDsId: int = None, + dstdir: str = None, + freesize: int = None, + start_time: int = None, + stop_time: int = None, + isoverwrite: int = None, + camlistid: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_event_export_info_from_recording_server(self, + start_time: int = None, + stop_time: int = None, + camlistid: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Export' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetEvtExpInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def load_event_mount(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Recording.Mount' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def redirect_webapi_to_target_ds(self, + dsId: int = None, + webAPI: Any = None) -> dict[str, object] | str: # TODO not working + + """webAPI Array of + + Example: + webAPI={"api": "SYNO.SurveillanceStation.AddOns", "version": 1, "method": + "List"} """ + + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Redirect'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def modify_share_privilege(self, + privSet: int = None, + shareName: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def apply_option_settings(self, + central_auto_video_relay: bool = None, + central_enable: bool = None, + central_mode: str = None, + central_rec_mask_mode: bool = None, + central_rec_sync_time: bool = None, + nvr_enable: bool = None, + nvr_lang: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ApplyOption'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_cms_info(self, + isPolling: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_log_recording_data_from_target_ds(self, + syncType: int = None, + syncTargetId: int = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DoSyncData'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_samba_service(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckSambaEnabled'} + + return self.request_data(api_name, api_path, req_param) + + def check_if_samba_on_and_rec_enabled(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'BatCheckSambaService'} + + return self.request_data(api_name, api_path, req_param) + + def get_encoded_single_image_of_camera(self, + camId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetMDSnapshot'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_cms_status(self, + camId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCMSStatus'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_smb_service(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnableSamba'} + + return self.request_data(api_name, api_path, req_param) + + def notify_slave_ds_to_disconnect(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'NotifyCMSBreak'} + + return self.request_data(api_name, api_path, req_param) + + def lock_recording_server_prevent_setting_change(self, + locked: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'LockSelf'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_ds_into_recording_server(self, + adminUsername: str = None, + adminPasswd: str = None, + central_rec_mask_mode: str = None, + central_rec_sync_time: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnableCMS'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unpair_recording_servers(self, + adminUsername: str = None, + key: str = None, + mac: str = None, + cmsMode: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'UnPair'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_free_memory_size(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetFreeSpace'} + + return self.request_data(api_name, api_path, req_param) + + def handle_slave_ds(self, + lock: bool = None, + adminUsername: str = None, + key: str = None, + mac: str = None, + masterAuthKey: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_target_ds_info(self, + slaveDslp: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Test'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def logout_slave_ds(self, + adminUsername: str = None, + key: str = None, + mac: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Logout'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def pair_slave_ds(self, + dsname: str = None, + slaveDslp: str = None, + port: int = None, + masterAuthKey: str = None, + model: str = None, + mac: str = None, + cms_locked: bool = None, + cms_masked: bool = None, + cms_sync_time: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Pair'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def login_slave_ds(self, + adminUsername: str = None, + key: str = None, + mac: str = None, + masterAuthKey: str = None, + hostName: str = None, + hostPort: int = None, + ignoreAuthError: str = None, + hostDisconnect: bool = None, + blUpdateVolSpace: bool = None, + enable_rec: bool = None, + cms_locked: bool = None, + cms_masked: bool = None, + cms_sync_time: bool = None) -> dict[str, object] | str: # TODO not working + + """2.3.15.9 API Error Code + Code Description + 400 Execution failed. + 401 Invalid parameter. + 415 message connect failed. """ + + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Login'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_slave_ds(self, + slavedsName: str = None, + slavedsModel: str = None, + slavedsPort: int = None, + slavedsVersion: str = None, + slavedsMaxCamNum: int = None, + slavedsId: str = None, + slavedsIP: str = None, + slavedsEnable: int = None, + slavedsCamCnt: bool = None, + adminUsername: str = None, + adminPasswd: str = None, + cms_locked: bool = None, + cms_masked: bool = None, + cms_sync_time: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.CMS.GetDsStatus' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def load_slave_ds_list(self, + blNeedStatus: bool = None, + blGetSortInfo: bool = None, + blRuntimeInfo: bool = None, + dslds: str = None, + sortInfo: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.CMS.SlavedsList' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def count_number_of_logs(self, + slavedsName: str = None, + start: int = None, + limit: int = None, + level: str = None, + filterCamera: str = None, + cameraIds: str = None, + dsfrom: int = None, + to: int = None, + keyword: str = None, + keywordDsId: str = None, + time2String: str = None, + dsId: str = None, + srcType: int = None, + timezoneOffset: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategory'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def clear_selected_logs(self, + blClearAll: bool = None, + level: int = None, + dsId: int = None, + filterCamera: str = None, + cameraIds: str = None, + dsfrom: int = None, + to: int = None, + keyword: str = None, + keywordDsId: str = None, + srcType: int = None, + timezoneOffset: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Clear'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_information_log(self, + start: int = None, + limit: int = None, + level: str = None, + filterCamera: str = None, + cameraIds: str = None, + dsfrom: int = None, + to: int = None, + keyword: str = None, + keywordDsId: str = None, + time2String: str = None, + dsId: int = None, + srcType: int = None, + all: bool = None, + blIncludeRecCnt: str = None, + blIncludeAuInfo: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_advanced_settings_logs(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSetting'} + + return self.request_data(api_name, api_path, req_param) + + def set_advanced_setting_logs(self, + data: Any = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetSetting', 'data': data } + + """data example: + + data=[{"SSLogType":321912835,"enable":1},{"SSLogType":321912836,"enable":0}]""" + + return self.request_data(api_name, api_path, req_param) + + def load_license_data(self, + num_only: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.License' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load', 'num_only': num_only} + + return self.request_data(api_name, api_path, req_param) + + def check_license_quota(self, + camList: Any = None, + camServerId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.License' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckQuota'} + + """camList example: + + camList = [{"ip": "10.13.22.141", "model": "DCS-3110", "vendor": "DLink", "port": 80}]""" + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_http_video_stream(self, + writeHeader: bool = None, + analyevent: bool = None, + mountId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Stream' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EventStream'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_action_rule(self, + id: int = None, + name: str = None, + ruleType: int = None, + actType: int = None, + evtSrc: int = None, + evtDsId: int = None, + evtDevId: int = None, + evtId: int = None, + evtItem: int = None, + evtMinIntvl: int = None, + Actions: Any = None, + actSchedule: str = None, + Id: int = None, + actSrc: int = None, + actDsId: int = None, + actDevId: int = None, + actId: int = None, + actTimes: int = None, + actTimeUnit: int = None, + actTimeDur: int = None, + actItemId: int = None, + actRetPos: int = None, + extUrl: str = None, + userName: str = None, + password: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def download_action_rule(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DownloadHistory'} + + return self.request_data(api_name, api_path, req_param) + + def send_data_2_player(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SendData2Player'} + + return self.request_data(api_name, api_path, req_param) + + def delete_all_histories_of_action_rule(self, idList: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteHistory', 'idList': idList} + + return self.request_data(api_name, api_path, req_param) + + def list_action_rules(self, start: str = None, limit: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List', 'Start': start, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def disable_action_rules(self, idList: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable', 'idList': idList} + + return self.request_data(api_name, api_path, req_param) + + def enable_action_rules(self, idList: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable', 'idList': idList} + + return self.request_data(api_name, api_path, req_param) + + def list_history_action_rules(self, start: int = None, limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListHistory', 'start': start, 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def delete_action_rule(self, idList: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.ActionRule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete', 'idList': idList} + + return self.request_data(api_name, api_path, req_param) + + def get_list_of_emaps(self, + start: int = None, + limit: str = None, + emapIds: int = None, + includeItems: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Emap' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_specific_emaps_setting(self, + emapIds: int = None, + includeImage: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Emap' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_emap_image(self, + filename: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Emap.Image' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_autorized_ds_token(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetRegisterToken'} + + return self.request_data(api_name, api_path, req_param) + + def set_message_event(self, + eventTypes: str = None, + subject: str = None, + content: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetCustomizedMessage'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_message_event(self, + eventTypes: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetCustomizedMessage'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_notification_sender_name(self, + ss_pkg_name: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetVariables'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_notification_sender_name(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetVariables'} + + return self.request_data(api_name, api_path, req_param) + + def set_advanced_notification_setting(self, + blSyncDSMNotify: bool = None, + blCompactMsg: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetAdvSetting'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_advanced_notification_setting(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Notification' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetAdvSetting'} + + return self.request_data(api_name, api_path, req_param) + + def send_test_mesg_to_primary_secondary_phone(self, + smsEnable: bool = None, + smsMethod: int = None, + smsProvider: str = None, + userName: str = None, + password: str = None, + confirmPassword: str = None, + primaryPhoneCode: str = None, + primaryPhonePrefix: str = None, + secondaryPhoneCode: str = None, + secondaryPhonePrefix: str = None, + secondaryPhoneNumber: str = None, + setMinMessageInterval: bool = None, + minMessageInterval: int = None, + hasSysSms: bool = None, + apiId: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.SMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SendTestMessage'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_setting_notification_sms(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.SMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSetting'} + + return self.request_data(api_name, api_path, req_param) + + def set_sms_service_setting(self, + smsEnable: bool = None, + smsMethod: int = None, + smsProvider: str = None, + userName: str = None, + password: str = None, + confirmPassword: str = None, + primaryPhoneCode: str = None, + primaryPhonePrefix: str = None, + secondaryPhoneCode: str = None, + secondaryPhonePrefix: str = None, + secondaryPhoneNumber: str = None, + setMinMessageInterval: bool = None, + minMessageInterval: int = None, + hasSysSms: bool = None, + apiId: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.SMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetSetting'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def send_test_sms(self, + attachSnapshot: bool = None, + enableInterval: bool = None, + mobileEnable: bool = None, + msgInterval: str = None, + primaryEmail: str = None, + secondaryEmail: str = None, + synoMailEnable: bool = None, + mail_recipient: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.SMS' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SendTestMessage'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def send_test_mail(self, + attachSnapshot: bool = None, + enableInterval: bool = None, + mobileEnable: bool = None, + msgInterval: str = None, + primaryEmail: str = None, + secondaryEmail: str = None, + synoMailEnable: bool = None, + mail_recipient: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.PushService' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SendVerificationMail'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_mobile_paired_devices(self, + attachSnapshot: bool = None, + enableInterval: bool = None, + mobileEnable: bool = None, + msgInterval: str = None, + primaryEmail: str = None, + secondaryEmail: str = None, + synoMailEnable: bool = None, + mail_recipient: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.PushService' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListMobileDevice'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unpair_device(self, + targetIds: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.PushService' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetSetting'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_controller_access_schedule(self, + targetIds: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetAccessControlControllerSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_camera_alarm_schedule(self, + cameraId: int = None, + alarmdx: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCameraAlarmSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_sys_dependent_schedule(self, + eventGroupTypes: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSystemDependentSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_batch_schedule(self, + eventTypes: str = None, + schedule: Any = None, + cameraIds: str = None, + cameraGroupIds: str = None, + filter: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetBatchSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_access_ctrl_door_schedule(self, + doorId: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetAccessControlDoorSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_camera_schedule(self, + cameraId: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCameraSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_sys_dependent_schedule(self, + eventType: int = None, + schedule: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetSystemDependentSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_controller_access_schedule(self, + eventType: int = None, + schedule: Any = None, + doorId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetAccessControlSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_camera_schedule(self, + eventType: int = None, + schedule: Any = None, + cameraId: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Schedule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetCameraSchedule'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_notification_email_string(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.Email' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSetting'} + + return self.request_data(api_name, api_path, req_param) + + def set_adv_tab_info_filter(self, + X: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.Email' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Set'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_sms_service_provider(self, + providerName: str = None, + providerPort: int = None, + providerUrl: str = None, + providerTemplate: str = None, + providerSepChar: str = None, + providerNeedSSL: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.SMS.ServiceProvider' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Create'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_sms_provider(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Notification.SMS.ServiceProvider' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + return self.request_data(api_name, api_path, req_param) + + def delete_sms_service_provider(self, + providerName: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Notification.SMS.ServiceProvider' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_addson_to_update(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetUpdateInfo'} + + return self.request_data(api_name, api_path, req_param) + + def enable_specific_addon(self, + service: int = None, + servicename: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_specific_addon_update_info(self, + service: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckUpdateInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_specific_addon_info(self, + service: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_total_addon_info(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + return self.request_data(api_name, api_path, req_param) + + def update_addon_package(self, + service: int = None, + filePath: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Update'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_addon_status(self, + service: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CheckEnableDone'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_addon(self, + service: int = None, + serviceName: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_addon_autoupdate(self, + service: int = None, + BlEnabled: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AddOns' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetAutoUpdate'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_specific_camera_recording_server(self, + camIdList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RecServClear'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_camera_event_analytic(self, + camIdList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EventCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_selected_events(self, + dsIdList: str = None, + idList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ClearSelected'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_specific_camera_events(self, + camIdList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EventCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_analytic_history(self, + camIdList: str = None, + typeListstring: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_analytic_history_by_filter(self, + camIdList: str = None, + dsId: int = None, + lock: int = None, + typeList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RecServerEnum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unklock_selected_events(self, + dsId: int = None, + idList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_camera_analytic_trigger(self, + trigCamIdList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Trigger'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def flush_event_header(self, + eventId: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EventFlushHeader'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_selected_events(self, + dsId: int = None, + idList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_analytic_event_from_rec_server(self, + camIdList: str = None, + idList: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RecServerEventCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_analytic_settings(self, + camId: int = None, + type: int = None, + showFrame: bool = None, + showLine: bool = None, + showVirtualFence: bool = None, + beep: bool = None, + sens: int = None, + dwellTime: int = None, + direction: int = None, + objSize: int = None, + region: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Alert.Setting' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_if_snapshot_exist(self, + id: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ChkFileExist'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_snapshot_modification(self, + id: int = None, + createCopy: bool = None, + width: int = None, + height: int = None, + byteSize: int = None, + imageData: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Edit'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def count_snapshot_by_category(self, + keyword: str = None, + dsfrom: int = None, + to: int = None, + timezoneOffset: int = None, + byteSize: int = None, + imageData: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategory'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_any_locked_snapshot(self, + id: str = None, + dsfrom: int = None, + to: int = None, + keyword: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ChkContainLocked'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_snapshot_by_filter(self, + dsfrom: int = None, + to: int = None, + keyword: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'UnlockFiltered'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_snapshot_information(self, + idList: str = None, + start: int = None, + limit: int = None, + dsfrom: int = None, + to: int = None, + keyword: str = None, + imgSize: int = None, + blIncludeAuInfo: bool = None, + blIncludeRecCnt: bool = None, + camId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_snapshot(self, + objList: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def take_snapshot(self, + dsId: int = None, + camId: int = None, + blSave: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'TakeSnapshot'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_snapshot_setting_function(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetSetting'} + + return self.request_data(api_name, api_path, req_param) + + def delete_snapshot_by_filter(self, + deleteAllCommand: bool = None, + dsfrom: int = None, + to: int = None, + keyword: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteFiltered'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + if key == 'dsfrom': + req_param[str('from')] = val + else: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_snapshot_image(self, + id: int = None, + imgSize: int = None) -> dict[str, object] | str: # TODO to modify for download? + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'LoadSnapshot'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_snapshot_image(self, + objList: Any = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def downld_single_snapshot(self, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Download'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_new_snapshot_setting(self, + dispSnapshot: bool = None, + dispDuration: int = None, + limitTotalSize: bool = None, + limitSizeInGb: int = None, + addTimestamp: bool = None, + timestampPosition: int = None)-> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveSetting'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_snapshot(self, + camName: str = None, + createdTm: int = None, + width: int = None, + height: int = None, + byteSize: int = None, + imageData: str = None)-> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def check_snapshot_status(self, + dispSnapshot: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.SnapShot' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ChkSnapshotValid'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_visualstation(self, + vslist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def update_vs_network_config(self, + vsMAc: str = None, + ip: str = None, + mask: str = None, + gateway: str = None, + blDhcp: bool = None, + name: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ReqNetConfig'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_visualstation_by_id(self, + vslist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_vs_owner_info(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + return self.request_data(api_name, api_path, req_param) + + def unlock_visualstation_by_id(self, + vslist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_visualstation_by_id(self, + vslist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_specific_visualstation(self, + vslist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_layout_visualstation(self, + vsId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Layout' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_layout_information(self, + id: int = None, + vsId: int = None, + name: str = None, + canGrpId: int = None, + isDefault: int = None, + isFixAspectRatio: int = None, + layoutType: int = None, + channelList: Any = None, + customPosList: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Layout' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_layout_visualstation(self, + id: int = None, + vsId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Layout' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def clear_visualstation_search_result(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Start'} + + return self.request_data(api_name, api_path, req_param) + + def get_visualstation_ip_info(self, + ip: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SearchIP'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def stop_previous_visualstation_search(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Stop'} + + return self.request_data(api_name, api_path, req_param) + + def get_visualstation_list(self, + offset: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.VisualStation.Layout' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'InfoGet'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_number_of_controller(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetUpdateInfo'} + + return self.request_data(api_name, api_path, req_param) + + def get_cardholder_count(self, filterKeyword: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategoryCardHolder'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enum_all_controllers_logger(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnumLogConfig'} + + return self.request_data(api_name, api_path, req_param) + + def get_cardholder_photo(self, + photo_name: str = None, + isRedirectCgi: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCardholderPhoto'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_log_count(self, + start: int = None, + limit: int = None, + filterType: int = None, + filterEventSource: Any = None, + filterSource: int = None, + filterEventSourceItem: int = None, + filterTimeFrom: int = None, + filterTimeTo: int = None, + filterKeyword: str = None, + timezoneOffset: int = None, + doorIds: str = None, + eventTypes: str = None, + update: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategoryLog'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_cardholder_info(self, + start: int = None, + limit: int = None, + filterKeyword: str = None, + filterStatus: int = None, + filterCtrlerId: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnumCardHolder'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def retrieve_last_access_credential(self, + ctrlerId: int = None, + idPtId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'RetrieveLastCard'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_disable_controller(self, + blEnable: bool = None, + arrayJson: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnableCtrler'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def acknowledge_all_alarm_level_log(self, + start: int = None, + limit: int = None, + filterEventSource: Any = None, + filterSource: int = None, + filterEventSourceItem: str = None, + filterTimeFrom: int = None, + filterKeyword: str = None, + doorIds: str = None, + eventTypes: str = None, + update: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AckAlarm'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def modify_controller_logger_config(self, + data: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveLogConfig'} + + """data example: + + data={"log_evt":"11111111111111111111111111111111111111", + "id": 97, "log_alarm":"00111111111111111111111111111111111111"}""" + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_controller_settings(self, + arrayJson: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + """arrayJson example: + + arrayJson="[{\"enable\":true,\"id\":97,\"name\":\"ctrler1\",\"host\":\"10.13.12.173\",\"port\":80, + \"model\":\"A1001\",\"username\":\"root\",\"password\":\"Q__Q-__-\",\"time_server\": + \"SurveillanceStation\",\"time_zone\":\"Fiji\",\"door\":[{\"id\":231,\"name\":\"FrontDoor\", + \"enable_cam\":true,\"cam_ds_id\":0,\"cam_id\":13}]}]\" """ + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def download_filtered_logs(self, + start: int = None, + limit: int = None, + filterType: int = None, + filterEventSource: int = None, + filterSource: int = None, + filterEventSourceItem: str = None, + filterTimeFrom: int = None, + filterTimeTo: int = None, + filterKeyword: str = None, + doorIds: str = None, + eventTypes: str = None, + update: int = None) -> dict[str, object] | str: # TODO to modify for download? + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DownloadLog'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_door_name_from_controller(self, + ctrlerId: int = None, + ip: str = None, + port: int = None, + userName: str = None, + password: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetDoorNames'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def test_connection_and_authentication(self, + ctrlerId: int = None, + ip: str = None, + port: int = None, + userName: str = None, + password: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'TestConnect'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_controller_list_info(self, + start: int = None, + limit: int = None, + update: int = None, + blIncludeRecCnt: bool = None, + blIncludeAuInfo: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_cardholder_setting(self, + arrayJson: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveCardHolder'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_door_info(self, + DoorIds: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListDoor'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def clear_logs_surveillance_station(self, + filterType: int = None, + filterEventSource: Any = None, + filterSource: int = None, + filterEventSourceItem: str = None, + filterTimeFrom: int = None, + filterTimeTo: int = None, + filterKeyword: str = None, + doorIds: str = None, + eventTypes: str = None, + update: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ClearLog'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_all_user_privilege(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListPrivilege'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def manual_lock_operation(self, + doorId: int = None, + operation: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DoorControl'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_user_door_priv_setting(self, + arrayJson: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SavePrivilege'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_all_logs(self, + start: int = None, + limit: int = None, + filterType: int = None, + filterEventSource: Any = None, + filterSource: int = None, + filterEventSourceItem: str = None, + filterTimeFrom: int = None, + filterTimeTo: int = None, + filterKeyword: str = None, + timezoneOffset: int = None, + doorIds: str = None, + eventTypes: str = None, + update: int = None, + blIncludeRecCnt: bool = None, + blIncludeAuInfo: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListLog'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_selected_controller(self, ids: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def retrieve_data_from_controller(self, ctrlerId: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Retrieve'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def block_cardholder(self, arrayJson: str = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'BlockCardHolder'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_controller_count(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategory'} + + return self.request_data(api_name, api_path, req_param) + + def start_controller_search(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Start'} + + return self.request_data(api_name, api_path, req_param) + + def get_controller_search_result(self, + pid: int = None, + offset: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.AxisAcsCtrler.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'InfoGet'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enumerate_digital_output(self, + camId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.DigitalOutput' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def save_digital_output_parameters(self, + camId: int = None, + idx: int = None, + keep_setting: bool = None, + normal_state: int = None, + trigger_state: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.DigitalOutput' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def long_polling_digital_output_status(self, + camId: int = None, + idx: int = None, + keep: bool = None, + setNormalCap: bool = None, + normal: int = None, + trigger: bool = None, + timeOut: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.DigitalOutput' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'PollState'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def trigger_external_event(self, + eventId: int = None, + eventName: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.ExternalEvent' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Trigger'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_list_io_modules(self, + start: int = None, + limit: int = None, + blFromList: bool = None, + ownerDsId: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_io_port_list(self, + Id: int = None, + Port: int = None, + IP: str = None, + User: str = None, + Pass: str = None, + Vendor: str = None, + Model: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnumPort'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_supported_list_io_modules(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnumVendorModel'} + + return self.request_data(api_name, api_path, req_param) + + def save_setting_io_module(self, + name: str = None, + id: int = None, + ownerDsId: int = None, + vendor: str = None, + model: str = None, + ip: str = None, + port: int = None, + userName: str = None, + enabled: bool = None, + status: int = None, + timeServer: str = None, + passWord: str = None, + ntpEnable: bool = None, + DIOdata: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_io_modules(self, + iomlist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_io_modules(self, + iomlist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_io_modules(self, + iomlist: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def test_connection_to_io_module(self, + id: int = None, + port: str = None, + ip: str = None, + userName: str = None, + passWord: str = None, + model: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'TestConn'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_capability_io_module(self, + vendor: str = None, + model: str = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCap'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def configure_io_port_setting(self, + id: int = None, + DIOdata: Any = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'PortSetting'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def poll_trigger_state_io_module(self, + Id: int = None, + list: Any = None, + timeOut: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'PollingDI'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def poll_do_trigger_module(self, + id: int = None, + idx: int = None, + normal: int = None, + trigger: bool = None, + timeOut: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'PollingDO'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_number_of_devices(self) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetDevNumOfDs'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_category_count_io_module(self, + start: int = None, + limit: int = None, + ownerDsId: int = None, + blFromList: bool = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.IOModule' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountByCategory'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_search_io_module(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.IOModule.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Start'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_search_io_module_info(self, + pid: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.IOModule.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'InfoGet'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_current_camera_status(self, + id_list: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Camera.Status' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'OneTime'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enum_preset_camera_list(self, + cameraId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_preset_camera_capability(self, + cameraId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def record_current_camera_position(self, + cameraId: int = None, + position: int = None, + speed: int = None, + name: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetPreset'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_list_preset_camera(self, + cameraId: Any = None, + position: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DelPreset'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def go_specific_preset_by_given_speed(self, + cameraId: Any = None, + position: int = None, + speed: int = None, + type: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Execute'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def set_current_camera_position(self, + cameraId: Any = None, + bindPosition: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Preset' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SetHome'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enum_patrol_list(self, + cam: Any = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enum_patrol_name_list(self, + camId: Any = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnumPartial'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def load_patrol_detail(self, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def add_or_modify_patrol(self, + camId: Any = None, + id: int = None, + stayTime: int = None, + speed: int = None, + name: str = None, + presetList: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_specific_patrol(self, + camId: Any = None, + patrolId: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def run_patrol(self, + camId: Any = None, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Execute'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def stop_patrol(self, + camId: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.PTZ.Patrol' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Stop'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_camera_search_process(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Start'} + + return self.request_data(api_name, api_path, req_param) + + def get_camera_search_info(self, + pid: int = None, + offset: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Camera.Search' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def toggle_home_mode(self, + on: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.HomeMode' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Switch'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_home_mode_settings(self, + need_mobiles: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.HomeMode' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_transaction_list(self, + filterIds: str = None, + filterDsIds: str = None, + filterEnable: bool = None, + filterStatus: int = None, + start: int = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Device' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_all_transaction_list(self, + filterIds: str = None, + dsId: int = None, + filterTimeFrom: Any = None, + filterStatus: int = None, + filterLock: bool = None, + filterTimeTo: Any = None, + filterTimeRangeIntersect: bool = None, + filterKeyword: str = None, + start: int = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enum'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_history_records(self, + filterIds: str = None, + dsId: int = None, + filterStatus: int = None, + filterLock: bool = None, + filterTimeFrom: Any = None, + filterTimeTo: Any = None, + filterTimeRangeIntersect: bool = None, + filterKeyword: str = None, + start: int = None, + limit: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_history_records(self, + filterIds: str = None, + dsId: int = None, + filterStatus: int = None, + filterLock: bool = None, + filterTimeFrom: Any = None, + filterTimeTo: Any = None, + filterTimeRangeIntersect: bool = None, + filterKeyword: str = None, + start: int = None, + limit: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_history_records(self, + filterIds: str = None, + dsId: int = None, + filterStatus: int = None, + filterLock: bool = None, + filterTimeFrom: Any = None, + filterTimeTo: Any = None, + filterTimeRangeIntersect: bool = None, + filterKeyword: str = None, + start: int = None, + limit: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_session_with_specified_session_id(self, + device_name: str = None, + session_id: str = None, + timeout: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Begin'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def complete_session_with_specified_id(self, + device_name: str = None, + session_id: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Complete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def cancel_session_with_specified_session_id(self, + device_name: str = None, + session_id: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Cancel'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def carry_data_into_session_id(self, + device_name: str = None, + session_id: str = None, + content: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Transactions.Transaction' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'AppendData'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def add_edit_active_vault_task(self, + blCustomFolder: bool = None, + blLimitBySize: bool = None, + blRotateFile: bool = None, + blSrcRecNoOverlap: bool = None, + blUseRecDet: bool = None, + camId: Any = None, + camInfo: Any = None, + dayLimit: int = None, + didCode: str = None, + dsSerial: str = None, + execTime: Any = None, + hostname: str = None, + id: int = None, + name: str = None, + passwd: str = None, + port: str = None, + recEndTm: Any = None, + recMode: str = None, + recSchedule: str = None, + recStartTm: Any = None, + schedule: str = None, + storagePath: str = None, + type: int = None) -> dict[str, object] | str: # TODO to check + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def login_source_server_get_info(self, + port: str = None, + hostname: str = None, + protocol: bool = None, + username: str = None, + passwd: str = None, + archId: int = None, + didCode: str = None, + srcDsId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'LoginSourceDS'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_archive_vault_task(self, + id: int = None, + keepRec: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_exist_archive_vault(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_archive_vault_task(self, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_archive_vault_task(self, + id: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DisableTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_archive_vault_batchedit_task(self, + taskIds: str = None, + attrs: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'BatchEditTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_batch_edit_progress(self, + pid: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'BatchEditProgress'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_batchedit_proress_info(self, + pid: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetBatchEditProgress'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def clean_batchedit_progress_data(self, + pid: int = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.Archiving.Pull' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'BatchEditProgressDone'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_youtube_live_broadcast_setting(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.YoutubeLive' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Load'} + + return self.request_data(api_name, api_path, req_param) + + def set_youtube_live_broadcast_info(self, + rtmp_path: str = None, + key: str = None, + cam_id: int = None, + stream_profile: int = None, + live_on: bool = None) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.YoutubeLive' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Save'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def close_youtube_live_broadcast(self) -> dict[str, object] | str: + api_name = 'SYNO.SurveillanceStation.YoutubeLive' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CloseLive'} + + return self.request_data(api_name, api_path, req_param) + + def get_deep_video_analytic(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListTask'} + + return self.request_data(api_name, api_path, req_param) + + def create_edit_DVA_task(self, + analyze_type: int = None, + actFromHost: bool = None, + name: str = None, + camera_id: int = None, + enable: bool = None, + enable_recording: bool = None, + pre_rec_time: int = None, + post_rec_time: int = None, + event_integration: int = None, + region_type: int = None, + det_region_cnt: int = None, + det_region: int = None, + people_mode: int = None, + reset_cnt_frequency: int = None, + reset_weekday: int = None, + reset_date: int = None, + reset_time_minute: int = None, + reset_time_hour: int = None, + fence_dir_flag: int = None, + people_display_pos: int = None, + stream_profile: int = None, + people_enable_stay_max: bool = None, + intrusion_detect_target: int = None, + min_obj_size: Any = None, + min_obj_size_option: int = None, + enable_min_duration: int = None, + people_display_info: int = None, + people_enter: int = None, + people_stay_max: int = None, + people_region: str = None, + people_hint_pos: str = None, + blEditMode: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_dva_task(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_dva_task(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnableTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_dva_task(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DisableTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def reset_counter_people_counting_task(self, + taskId: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ResetPplCntCounter'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_people_enter_leave_count(self, + ids: str = None, + timeStart: str = None, + timeEnd: str = None, + timezone: int = None, + ) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Report' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_people_count_of_day(self, + ids: str = None, + interval: int = None, + intervalUnit: int = None, + timezone: int = None, + timestamp: int = None, + blOccupancy: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Report' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetReport'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_people_counting_task(self, + taskList: str = None, + limit: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_recording_file_of_detection(self, + slaveDsParam: str = None, + deleteMethod: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_info_of_task_and_frame(self, + eventId: int = None, + taskId: int = None, + blAlertEvt: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetAnalyticResult'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_recording_file_of_detection(self, + dsId: int = None, + idList: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_recording_file_of_detection(self, + dsId: int = None, + idList: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.Recording' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_info_people_counting_task(self) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_people_counting_task(self, + enable: bool = None, + task_ids: str = None, + owner_ds_id: int = None, + name: str = None, + people_display_info: str = None, + people_enable_stay_max: int = None, + reset_cnt_frequency: int = None, + resert_date: int = None, + resert_weekday: int = None, + resert_tome_hour: int = None, + resert_tome_minute: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Create'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def modify_setting_of_people_counting_task(self, + enable: bool = None, + id: int = None, + task_ids: str = None, + name: str = None, + people_display_info: int = None, + people_enable_max: int = None, + reset_cnt_frequency: int = None, + resert_date: int = None, + resert_weekday: int = None, + resert_tome_hour: int = None, + resert_tome_minute: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Edit'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_task_group(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def start_count_people_task_in_groups(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Enable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def stop_count_people_task_in_groups(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Disable'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_number_counting_task_group(self, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetPeopleCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_recording_file_result(self, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.IVA.TaskGroup' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ResetPeopleCount'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_face_list_task(self, + ids: str = None, + ownerDsId: int = None, + blOnlyEnableDs: bool = None, + ) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_or_edit_task(self, + id: int = None, + id_on_rec_server: int = None, + camera_id: int = None, + camera_id_on_rec: int = None, + owner_ds_id: int = None, + enable: bool = None, + blEditMode: bool = None, + stream_profile: int = None, + name: str = None, + similarity: float = None, + allowed_color: int = None, + allowed_list: Any = None, + blocked_color: int = None, + blocked_list: Any = None, + vip_color: int = None, + vip_list: Any = None, + recognized_color: int = None, + unrecognized_color: int = None, + deleted: bool = None, + det_region: str = None, + det_region_cnt: int = None, + region_type: int = None, + display_info: int = None, + display_type: int = None, + frame_display_info: int = None, + enable_min_ogj_size: bool = None, + min_ogj_size: float = None, + post_rec_time: int = None, + pre_rec_time: int = None, + schedule: str = None, + scheduleOn: bool = None, + ignore_bad_quality: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_face_task(self, + ids: str = None, + keepRecording: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def enable_task_to_start_detection_recording(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EnableTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_task_to_stop_detection_recording(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DisableTask'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_task_with_privilege_to_watch(self, + ids: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListPlayableTsk'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_face_group(self, + name: str = None, + description: str = None, + update_registered_face: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CreateFaceGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def disable_face_grooup(self, + ids: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteFaceGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def edit_face_group(self, + name: str = None, + description: str = None, + update_registered_face: Any = None, + id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EditFaceGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_face_group_list(self, + id_only: bool = None, + filter: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListFaceGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def count_face_groups(self, + filter: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountFaceGroup'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def detect_faces_image(self, + image_data: str = None, + image_size: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DetectImageFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def create_registered_face(self, + account: str = None, + name: str = None, + description: str = None, + image_data: str = None, + image_size: int = None, + face: Any = None, + update_face_group: Any = None, + captured_face_id: int = None, + update_unrecognized_captured_face: bool = None, + append_image_data: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CreateRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_registered_face(self, + ids: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def edit_registered_face(self, + id: int = None, + account: str = None, + name: str = None, + description: str = None, + image_data: str = None, + image_size: int = None, + face: Any = None, + update_face_group: Any = None, + captured_face_id: int = None, + update_unrecognized_captured_face: bool = None, + append_image_data: bool = None, + ) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'EditRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_registered_face(self, + id_only: bool = None, + filter: Any = None, + append_image_data: bool = None, + ) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'ListRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def count_registered_face(self, + filter: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'CountRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def search_registered_face(self, + keywords: str = None, + append_image_data: bool = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SearchRegisteredFace'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_face_result_list(self, + filter: Any = None, + blIncludeSnapshot: bool = None, + blIncludeRegisteredFace: bool = None, + limit: int = None, + slaveDsParam: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'List'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_face_result(self, + filter: Any = None, + slaveDsParam: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Delete'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def lock_face_result(self, + filter: Any = None, + slaveDsParam: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Lock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def unlock_face_result(self, + filter: Any = None, + slaveDsParam: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Unlock'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_recording_file_of_face_info(self, + capturedFaceId: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetEventInfo'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def get_recognition_face_information(self, + taskId: int = None, + eventId: int = None, + startTime: int = None, + endTime: int = None, + blIncludeRegisteredFace: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'GetAnalyticResult'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def correct_face_result(self, + id: int = None, + registered_face_id: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'Correct'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def mark_face_result_as_stranger(self, + ids: str = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Face.Result' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'MarkAsStranger'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def add_new_bookmark(self, + id: int = None, + eventId: int = None, + cameraId: int = None, + archId: int = None, + name: str = None, + timestamp: Any = None, + comment: str = None, + ) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording.Bookmark' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'SaveBookmark'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def delete_bookmark(self, + bookmarkIds: Any = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording.Bookmark' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteBookmark'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + + def list_bookmark_detail(self, + offset: int = None, + limit: int = None, + cameraIds: str = None, + fromTime: int = None, + toTime: int = None) -> dict[str, object] | str: # TODO not working + api_name = 'SYNO.SurveillanceStation.Recording.Bookmark' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'DeleteBookmark'} + + for key, val in locals().items(): + if key not in ['self', 'api_name', 'info', 'api_path', 'req_param']: + if val is not None: + req_param[str(key)] = val + + return self.request_data(api_name, api_path, req_param) + diff --git a/synology_api/universal_search.py b/synology_api/universal_search.py new file mode 100644 index 0000000..2605c7c --- /dev/null +++ b/synology_api/universal_search.py @@ -0,0 +1,33 @@ +from __future__ import annotations +from typing import Optional, Any +from . import base_api + + +class UniversalSearch(base_api.BaseApi): + + def search(self, keyword: str) -> dict[str, object] | str: + api_name = 'SYNO.Finder.FileIndexing.Search' + info = self.gen_list[api_name] + api_path = info['path'] + + req_param = { + "query_serial": 1, + "indice": '[]', + "keyword": keyword, + "orig_keyword": keyword, + "criteria_list": '[]', + "from": 0, + "size": 10, + "fields": '["SYNOMDAcquisitionMake","SYNOMDAcquisitionModel","SYNOMDAlbum","SYNOMDAperture","SYNOMDAudioBitRate","SYNOMDAudioTrackNumber","SYNOMDAuthors","SYNOMDCodecs","SYNOMDContentCreationDate","SYNOMDContentModificationDate","SYNOMDCreator","SYNOMDDurationSecond","SYNOMDExposureTimeString","SYNOMDExtension","SYNOMDFSCreationDate","SYNOMDFSName","SYNOMDFSSize","SYNOMDISOSpeed","SYNOMDLastUsedDate","SYNOMDMediaTypes","SYNOMDMusicalGenre","SYNOMDOwnerUserID","SYNOMDOwnerUserName","SYNOMDRecordingYear","SYNOMDResolutionHeightDPI","SYNOMDResolutionWidthDPI","SYNOMDTitle","SYNOMDVideoBitRate","SYNOMDIsEncrypted"]', + "file_type": "", + "search_weight_list": '[{"field":"SYNOMDWildcard","weight":1},{"field":"SYNOMDTextContent","weight":1},{"field":"SYNOMDSearchFileName","weight":8.5,"trailing_wildcard":"true"}]', + "sorter_field": "relevance", + "sorter_direction": "asc", + "sorter_use_nature_sort": "false", + "sorter_show_directory_first": "true", + "api": "SYNO.Finder.FileIndexing.Search", + "method": "search", + "version": 1 + } + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/usb_copy.py b/synology_api/usb_copy.py new file mode 100644 index 0000000..e6f841a --- /dev/null +++ b/synology_api/usb_copy.py @@ -0,0 +1,47 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class USBCopy(base_api.BaseApi): + + def usb_copy_info(self, id: int = 1) -> dict[str, object] | str: + api_name = 'SYNO.USBCopy' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get', 'id': id} + + return self.request_data(api_name, api_path, req_param) + + def toggle_usb_copy(self, enable: bool = True, id: int = 1) -> dict[str, object] | str: + api_name = 'SYNO.USBCopy' + info = self.gen_list[api_name] + api_path = info['path'] + + if enable: + enable = 'enable' + elif not enable: + enable = 'disable' + else: + return 'enable must be True or False' + + req_param = {'version': info['maxVersion'], 'method': enable, 'id': id} + + return self.request_data(api_name, api_path, req_param) + + def logs(self, offset: int = 0, limit: int = 200) -> dict[str, object] | str: + api_name = 'SYNO.USBCopy' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_log_list', 'offset': offset, 'limit': limit, + 'log_filter': '{"log_desc_id_list":[0,1,2,3,10,11,100,101,102,103,104,105,1000]}'} + + return self.request_data(api_name, api_path, req_param) + + def global_settings(self) -> dict[str, object] | str: + api_name = 'SYNO.USBCopy' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get_global_setting'} + + return self.request_data(api_name, api_path, req_param) diff --git a/synology_api/virtualization.py b/synology_api/virtualization.py new file mode 100644 index 0000000..ebaf2f9 --- /dev/null +++ b/synology_api/virtualization.py @@ -0,0 +1,334 @@ +from __future__ import annotations +from typing import Optional, Any +from . import base_api + + +class Virtualization(base_api.BaseApi): + def __init__(self, + ip_address: str, + port: str, + username: str, + password: str, + secure: bool = False, + cert_verify: bool = False, + dsm_version: int = 7, + debug: bool = True, + otp_code: Optional[str] = None + ) -> None: + + super(Virtualization, self).__init__(ip_address, port, username, password, secure, cert_verify, + dsm_version, debug, otp_code) + + self._taskid_list: Any = [] + self._network_group_list: Any = [] + self._storages_list: Any = [] + self._host_operation_list: Any = [] + self._vm_guest_id_list: Any = [] + self._vm_guest_name_list: Any = [] + self._vm_created_taskid_list: Any = [] + + self.session.get_api_list('Virtualization') + + self.file_station_list: Any = self.session.app_api_list + + def get_task_list(self) -> list[str]: + api_name = 'SYNO.Virtualization.API.Task.Info' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + self._taskid_list = self.request_data(api_name, api_path, req_param) + + return self._taskid_list + + def clear_task(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Task.Info' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'clear'} + + if taskid is None: + return 'Enter a valid taskid, choose between get_task_list results' + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def get_task_info(self, taskid: str) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Task.Info' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + if taskid is None: + return 'Enter a valid taskid, choose between get_task_list results' + else: + req_param['taskid'] = taskid + + return self.request_data(api_name, api_path, req_param) + + def get_network_group_list(self) -> list[dict[str, object]]: + api_name = 'SYNO.Virtualization.API.Network' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + self._network_group_list = self.request_data(api_name, api_path, req_param) + + return self._network_group_list + + def get_storage_operation(self) -> list[str]: + api_name = 'SYNO.Virtualization.API.Storage' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + self._storages_list = self.request_data(api_name, api_path, req_param) + + return self._storages_list + + def get_host_operation(self) -> list[str]: + api_name = 'SYNO.Virtualization.API.Host' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + self._host_operation_list = self.request_data(api_name, api_path, req_param) + + return self._host_operation_list + + def get_vm_operation(self, additional: bool = False) -> list[dict[str, object]]: + api_name = 'SYNO.Virtualization.API.Guest' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list', 'additional': additional} + + info = self.request_data(api_name, api_path, req_param) + + for k, v in info['data']['guests']: + if k == 'guest_id': + self._vm_guest_id_list.append(info['data']['guests'][k]) + elif k == 'guest_name': + self._vm_guest_name_list.append(info['data']['guests'][k]) + + return info + + def get_specific_vm_info(self, + additional: Optional[str | list[str]] = None, + guest_id: Optional[str] = None, + guest_name: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + if guest_id is not None: + req_param['guest_id'] = guest_id + if additional is not None: + req_param['additional'] = additional + + return self.request_data(api_name, api_path, req_param) + + def set_vm_property(self, + guest_id: Optional[str] = None, + guest_name: Optional[str] = None, + autorun: Optional[int] = None, + description: Optional[str] = None, + new_guest_name: Optional[str] = None, + vcpu_num: Optional[int] = None, + vram_size: Optional[int] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'set'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + guest_id = None + if guest_id is not None: + req_param['guest_id'] = guest_id + guest_name = None + if autorun is not None and isinstance(autorun, int): + req_param['autorun'] = autorun + else: + return 'autorun value must be an integer 0 (off), 1 (last state) or 2 (on)' + + if description is not None and isinstance(description, str): + req_param['description'] = description + else: + return 'description must be a string, guest description' + + if new_guest_name is not None and isinstance(new_guest_name, str): + req_param['new_guest_name'] = new_guest_name + else: + return 'new_guest_name must be a string, new guest name' + + if vcpu_num is not None and isinstance(vcpu_num, int): + req_param['vcpu_num'] = vcpu_num + return 'vcpu_num must be an integer, specify cpu number' + + if vram_size is not None and isinstance(vram_size, int): + req_param['vram_size'] = vram_size + else: + return 'vram_size must be integer, specify ram size in MB' + + return self.request_data(api_name, api_path, req_param) + + def delete_vm(self, guest_id: Optional[str] = None, guest_name: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'delete'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + guest_id = None + if guest_id is not None: + req_param['guest_id'] = guest_id + guest_name = None + + return self.request_data(api_name, api_path, req_param) + + def vm_power_on(self, + guest_id: Optional[str] = None, + guest_name: Optional[str] = None, + host_id: Optional[str] = None, + host_name: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest.Action' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'poweron'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + guest_id = None + if guest_id is not None: + req_param['guest_id'] = guest_id + guest_name = None + + if host_id is not None: + req_param['host_id'] = host_id + host_name = None + if host_name is not None: + req_param['host_name'] = host_name + host_id = None + if host_id is None and host_name is None: + print('host_id and host_name are not specified, it will follow autoselect option from the cluster settings') + + return self.request_data(api_name, api_path, req_param) + + def vm_force_power_off(self, + guest_id: Optional[str] = None, + guest_name: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest.Action' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'poweroff'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + guest_id = None + if guest_id is not None: + req_param['guest_id'] = guest_id + guest_name = None + + return self.request_data(api_name, api_path, req_param) + + def vm_shut_down(self, guest_id: Optional[str] = None, guest_name: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest.Action' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'shutdown'} + + if guest_id is None and guest_name is None: + return 'Specify at least one guest_id or guest_name, you can get using get_vm_operation' + if guest_name is not None: + req_param['guest_name'] = guest_name + guest_id = None + if guest_id is not None: + req_param['guest_id'] = guest_id + guest_name = None + + return self.request_data(api_name, api_path, req_param) + + def get_images_list(self) -> dict[str, object]: + api_name = 'SYNO.Virtualization.API.Guest.Image' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'list'} + + return self.request_data(api_name, api_path, req_param) + + def delete_image(self, image_id: Optional[str] = None, image_name: Optional[str] = None) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest.Image' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create'} + + if image_id is None and image_name is None: + return 'Specify at least one of image_id or image_name' + if image_name is not None: + req_param['image_name'] = image_name + image_name = None + if image_id is not None: + req_param['image_id'] = image_id + image_name = None + + return self.request_data(api_name, api_path, req_param) + + def create_image(self, + auto_clean_task: bool = True, + storage_names: Optional[str] = None, + storage_ids: Optional[str] = None, + type: Optional[str] = None, + ds_file_path: Optional[str] = None, + image_name: Optional[str] = None + ) -> dict[str, object] | str: + api_name = 'SYNO.Virtualization.API.Guest.Image' + info = self.file_station_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'create', 'auto_clean_task': auto_clean_task} + + if storage_names is None and storage_ids is None: + return 'Specify at least one of storage_names or storge_ids' + if storage_ids is not None: + req_param['storage_ids'] = storage_ids + storage_names = None + if storage_names is not None: + req_param['storage_names'] = storage_names + storage_ids = None + + if type is None: + return 'Specify what type between disk, vdsm or iso' + if type is not None: + req_param['type'] = type + + if ds_file_path is None: + return 'Specify file path, the path should begin with a shared folder' + if ds_file_path is not None: + req_param['ds_file_path'] = ds_file_path + + if image_name is None: + return 'Specify image_name' + if image_name is not None: + req_param['image_name'] = image_name + + return self.request_data(api_name, api_path, req_param) + + # TODO create vitrual machine function diff --git a/synology_api/vpn.py b/synology_api/vpn.py new file mode 100644 index 0000000..b6569a9 --- /dev/null +++ b/synology_api/vpn.py @@ -0,0 +1,218 @@ +from __future__ import annotations +from typing import Optional +from . import base_api + + +class VPN(base_api.BaseApi): + + def settings_list(self) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'status_load'} + + return self.request_data(api_name, api_path, req_param) + + def active_connections_list(self, + sort: str = 'login_time', + sort_dir: str = 'DESC', + start: int = 0, + limit: int = 100 + ) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Management.Connection' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'enum', 'sort': sort, 'dir': sort_dir, 'start': start, + 'limit': limit} + + return self.request_data(api_name, api_path, req_param) + + def log_list(self, start: int = 0, limit: int = 50, prtltype: int = 0) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Management.Log' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'start': start, 'limit': limit, + 'prtltype': prtltype} + + return self.request_data(api_name, api_path, req_param) + + def network_interface_setting(self) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Management.Interface' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load'} + + return self.request_data(api_name, api_path, req_param) + + def security_autoblock_setting(self) -> dict[str, object] | str: + api_name = 'SYNO.Core.Security.AutoBlock' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'get'} + + return self.request_data(api_name, api_path, req_param) + + def permission_setting(self, start: int = 0, limit: int = 100) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Management.Account' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'action': 'enum', 'start': str(start), + 'limit': str(limit)} + + return self.request_data(api_name, api_path, req_param) + + def pptp_settings_info(self) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'serv_type': 'pptp'} + + return self.request_data(api_name, api_path, req_param) + + def openvpn_settings_info(self) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'serv_type': 'openvpn'} + + return self.request_data(api_name, api_path, req_param) + + def l2tp_settings_info(self) -> dict[str, object] | str: + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + req_param = {'version': info['maxVersion'], 'method': 'load', 'serv_type': 'l2tp'} + + return self.request_data(api_name, api_path, req_param) + + # TODO not working {'error': {'code': 600}, 'success': False} response + '''def pptp_settings_setup(self, serv_type='pptp', serv_enable=True, serv_ip='10.0.0.0', serv_range=5, auth_type=2, + auth_conn=3, mppe_type=1, mtu=1400, dns_check=False, dns='192.168.1.1'): + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + + if serv_enable: + serv_enable = 'true' + elif not serv_enable: + serv_enable = 'false' + else: + return 'serv_enable must be True or False' + + if dns_check: + dns_check = 'true' + elif not dns_check: + dns_check = 'false' + else: + return 'dns_check must be True or False' + + req_param = {'version': info['maxVersion'], 'method': 'apply', 'serv_type': serv_type, + 'serv_enable': serv_enable, 'serv_ip': serv_ip, 'serv_range': serv_range, + 'auth_type': auth_type, 'auth_conn': auth_conn, 'mppe_type': mppe_type, 'mtu': mtu, + 'dns_check': dns_check, 'dns': dns} + + return self.request_data(api_name, api_path, req_param)''' + + # TODO not working {'error': {'code': 600}, 'success': False} response + '''def openvpn_settings_setup(self, serv_type='openvpn', serv_enable=True, serv_ip='10.8.0.0', serv_range=5, + comp_enable=True, push_route_enable=False, tls_auth_key=False, verify_server_cn=False, + auth_conn=3, port=1194, protocol='udp', encryption='AES-256-CBC', authentication='SHA512', + enable_ipv6_server=False, ipv6_prefix='', mssfix_value=1450): + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + + if serv_enable: + serv_enable = 'true' + elif not serv_enable: + serv_enable = 'false' + else: + return 'serv_enable must be True or False' + + if comp_enable: + comp_enable = 'true' + elif not comp_enable: + comp_enable = 'false' + else: + return 'comp_enable must be True or False' + + if push_route_enable: + push_route_enable = 'true' + elif not push_route_enable: + push_route_enable = 'false' + else: + return 'push_route_enable must be True or False' + + if tls_auth_key: + tls_auth_key = 'true' + elif not tls_auth_key: + tls_auth_key = 'false' + else: + return 'tls_auth_key must be True or False' + + if verify_server_cn: + verify_server_cn = 'true' + elif not verify_server_cn: + verify_server_cn = 'false' + else: + return 'verify_server_cn must be True or False' + + if enable_ipv6_server: + enable_ipv6_server = 'true' + elif not enable_ipv6_server: + enable_ipv6_server = 'false' + else: + return 'enable_ipv6_server must be True or False' + + req_param = {'version': info['maxVersion'], 'method': 'apply', 'serv_type': serv_type, + 'serv_enable': serv_enable, 'serv_ip': serv_ip, 'serv_range': serv_range, + 'comp_enable': comp_enable, 'push_route_enable': push_route_enable, 'tls_auth_key': tls_auth_key, + 'verify_server_cn': verify_server_cn, 'auth_conn': auth_conn, 'port': port, 'protocol': protocol, + 'encryption': encryption, 'authentication': authentication, + 'enable_ipv6_server': enable_ipv6_server, 'ipv6_prefix': ipv6_prefix, 'mssfix_value': mssfix_value} + + return self.request_data(api_name, api_path, req_param)''' + + # TODO not working {'error': {'code': 600}, 'success': False} response + '''def l2tp_settings_setup(self, serv_type='l2tp', serv_enable=True, serv_ip='10.2.0.0', serv_range=5, + auth_type=2, auth_conn=3, mtu=1400, dns_check=False, dns='192.168.1.1', preshared_key='', + preshared_key_confirm='', sha2_truncbug=True, kernel_mode=True): + api_name = 'SYNO.VPNServer.Settings.Config' + info = self.gen_list[api_name] + api_path = info['path'] + + if serv_enable: + serv_enable = 'true' + elif not serv_enable: + serv_enable = 'false' + else: + return 'serv_enable must be True or False' + + if dns_check: + dns_check = 'true' + elif not dns_check: + dns_check = 'false' + else: + return 'dns_check must be True or False' + + if sha2_truncbug: + sha2_truncbug = 'true' + elif not sha2_truncbug: + sha2_truncbug = 'false' + else: + return 'sha2_truncbug must be True or False' + + if kernel_mode: + kernel_mode = 'true' + elif not kernel_mode: + kernel_mode = 'false' + else: + return 'kernel_mode must be True or False' + + req_param = {'version': info['maxVersion'], 'method': 'apply', 'serv_type': serv_type, + 'serv_enable': serv_enable, 'serv_ip': serv_ip, 'serv_range': serv_range, + 'auth_type': auth_type, 'auth_conn': auth_conn, 'mtu': mtu, 'dns_check': dns_check, 'dns': dns, + 'preshared_key': preshared_key, 'preshared_key_confirm': preshared_key_confirm, + 'sha2_truncbug': sha2_truncbug, 'kernel_mode': kernel_mode} + + return self.request_data(api_name, api_path, req_param)'''