mirror of
https://github.com/ProtoThis/python-synology.git
synced 2025-07-31 02:53:57 +00:00
@ -13,6 +13,7 @@ install:
|
||||
- pip install flake8
|
||||
before_script:
|
||||
- flake8 --show-source --builtins=_ synology
|
||||
- ./scripts/check_format.sh
|
||||
script:
|
||||
- python setup.py install
|
||||
- python setup.py sdist
|
||||
|
@ -17,6 +17,10 @@ Python API for Synology DSM
|
||||
:alt: Downloads
|
||||
:target: https://pypi.org/project/python-synology
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:alt: Formated with Black
|
||||
:target: https://github.com/psf/black
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
|
@ -12,10 +12,19 @@ from .helpers import SynoFormatHelper
|
||||
|
||||
|
||||
class SynologyDSM(object):
|
||||
#pylint: disable=too-many-arguments,too-many-instance-attributes
|
||||
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
||||
"""Class containing the main Synology DSM functions."""
|
||||
def __init__(self, dsm_ip, dsm_port, username, password,
|
||||
use_https=False, debugmode=False, dsm_version=6):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dsm_ip,
|
||||
dsm_port,
|
||||
username,
|
||||
password,
|
||||
use_https=False,
|
||||
debugmode=False,
|
||||
dsm_version=6,
|
||||
):
|
||||
# Store Variables
|
||||
self.username = username
|
||||
self.password = password
|
||||
@ -35,7 +44,6 @@ class SynologyDSM(object):
|
||||
# adding DSM Version
|
||||
self._dsm_version = dsm_version
|
||||
|
||||
|
||||
# Build Variables
|
||||
if self._use_https:
|
||||
# https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
|
||||
@ -45,14 +53,20 @@ class SynologyDSM(object):
|
||||
self.base_url = "https://%s:%s/webapi" % (dsm_ip, dsm_port)
|
||||
else:
|
||||
self.base_url = "http://%s:%s/webapi" % (dsm_ip, dsm_port)
|
||||
|
||||
|
||||
if self._dsm_version == 5:
|
||||
if self._use_https:
|
||||
self.storage_url = "https://%s:%s/webman/modules/StorageManager/storagehandler.cgi" % (dsm_ip, dsm_port)
|
||||
else:
|
||||
self.storage_url = "http://%s:%s/webman/modules/StorageManager/storagehandler.cgi" % (dsm_ip, dsm_port)
|
||||
self.storage_url = (
|
||||
"https://%s:%s/webman/modules/StorageManager/storagehandler.cgi"
|
||||
% (dsm_ip, dsm_port)
|
||||
)
|
||||
else:
|
||||
self.storage_url = (
|
||||
"http://%s:%s/webman/modules/StorageManager/storagehandler.cgi"
|
||||
% (dsm_ip, dsm_port)
|
||||
)
|
||||
|
||||
#pylint: enable=too-many-arguments,too-many-instance-attributes
|
||||
# pylint: enable=too-many-arguments,too-many-instance-attributes
|
||||
|
||||
def _debuglog(self, message):
|
||||
"""Outputs message if debug mode is enabled."""
|
||||
@ -61,10 +75,10 @@ class SynologyDSM(object):
|
||||
|
||||
def _encode_credentials(self):
|
||||
"""Encode user credentials to support special characters.."""
|
||||
# encoding special characters
|
||||
# encoding special characters
|
||||
auth = {
|
||||
'account': self.username,
|
||||
'passwd': self.password,
|
||||
"account": self.username,
|
||||
"passwd": self.password,
|
||||
}
|
||||
return urlencode(auth)
|
||||
|
||||
@ -77,23 +91,19 @@ class SynologyDSM(object):
|
||||
self._session = requests.Session()
|
||||
self._session.verify = False
|
||||
|
||||
api_path = "%s/auth.cgi?api=SYNO.API.Auth&version=2" % (
|
||||
self.base_url,
|
||||
)
|
||||
api_path = "%s/auth.cgi?api=SYNO.API.Auth&version=2" % (self.base_url,)
|
||||
|
||||
login_path = "method=login&%s" % (self._encode_credentials())
|
||||
|
||||
url = "%s&%s&session=Core&format=cookie" % (
|
||||
api_path,
|
||||
login_path
|
||||
)
|
||||
url = "%s&%s&session=Core&format=cookie" % (api_path, login_path)
|
||||
result = self._execute_get_url(url, False)
|
||||
|
||||
# Parse result if valid
|
||||
if result is not None:
|
||||
self.access_token = result["data"]["sid"]
|
||||
self._debuglog("Authentication Succesfull, token: " +
|
||||
str(self.access_token))
|
||||
self._debuglog(
|
||||
"Authentication Succesfull, token: " + str(self.access_token)
|
||||
)
|
||||
return True
|
||||
else:
|
||||
self.access_token = None
|
||||
@ -103,9 +113,7 @@ class SynologyDSM(object):
|
||||
def _get_url(self, url, retry_on_error=True):
|
||||
"""Function to handle sessions for a GET request."""
|
||||
# Check if we failed to request the url or need to login
|
||||
if self.access_token is None or \
|
||||
self._session is None or \
|
||||
self._session_error:
|
||||
if self.access_token is None or self._session is None or self._session_error:
|
||||
# Reset session error
|
||||
self._session_error = False
|
||||
|
||||
@ -128,10 +136,10 @@ class SynologyDSM(object):
|
||||
# Prepare Request
|
||||
self._debuglog("Requesting URL: '" + request_url + "'")
|
||||
if append_sid:
|
||||
self._debuglog("Appending access_token (SID: " +
|
||||
self.access_token + ") to url")
|
||||
request_url = "%s&_sid=%s" % (
|
||||
request_url, self.access_token)
|
||||
self._debuglog(
|
||||
"Appending access_token (SID: " + self.access_token + ") to url"
|
||||
)
|
||||
request_url = "%s&_sid=%s" % (request_url, self.access_token)
|
||||
|
||||
# Execute Request
|
||||
try:
|
||||
@ -147,15 +155,16 @@ class SynologyDSM(object):
|
||||
return json_data
|
||||
else:
|
||||
if json_data["error"]["code"] in {105, 106, 107, 119}:
|
||||
self._debuglog("Session error: " +
|
||||
str(json_data["error"]["code"]))
|
||||
self._debuglog(
|
||||
"Session error: " + str(json_data["error"]["code"])
|
||||
)
|
||||
self._session_error = True
|
||||
else:
|
||||
self._debuglog("Failed: " + resp.text)
|
||||
else:
|
||||
# We got a 404 or 401
|
||||
return None
|
||||
except: #pylint: disable=bare-except
|
||||
except: # pylint: disable=bare-except
|
||||
return None
|
||||
|
||||
def update(self, with_information=False):
|
||||
@ -168,7 +177,8 @@ class SynologyDSM(object):
|
||||
url = "%s/entry.cgi?api=%s&version=%s&method=getinfo" % (
|
||||
self.base_url,
|
||||
api,
|
||||
version)
|
||||
version,
|
||||
)
|
||||
self._information.update(self._get_url(url))
|
||||
|
||||
if self._utilisation is not None:
|
||||
@ -176,7 +186,8 @@ class SynologyDSM(object):
|
||||
url = "%s/entry.cgi?api=%s&version=1&method=get&_sid=%s" % (
|
||||
self.base_url,
|
||||
api,
|
||||
self.access_token)
|
||||
self.access_token,
|
||||
)
|
||||
self._utilisation.update(self._get_url(url))
|
||||
|
||||
if self._storage is not None:
|
||||
@ -185,16 +196,17 @@ class SynologyDSM(object):
|
||||
url = "%s/entry.cgi?api=%s&version=1&method=load_info&_sid=%s" % (
|
||||
self.base_url,
|
||||
api,
|
||||
self.access_token)
|
||||
self.access_token,
|
||||
)
|
||||
self._storage.update(self._get_url(url))
|
||||
else:
|
||||
url = "%s?action=load_info&_sid=%s" % (
|
||||
self.storage_url,
|
||||
self.access_token)
|
||||
self.access_token,
|
||||
)
|
||||
output = self._get_url(url)["data"]
|
||||
self._storage.update(output)
|
||||
|
||||
|
||||
@property
|
||||
def information(self):
|
||||
"""Getter for various Information variables."""
|
||||
@ -206,23 +218,20 @@ class SynologyDSM(object):
|
||||
url = "%s/entry.cgi?api=%s&version=%s&method=getinfo" % (
|
||||
self.base_url,
|
||||
api,
|
||||
version)
|
||||
version,
|
||||
)
|
||||
self._information = SynoDSMInformation(self._get_url(url))
|
||||
return self._information
|
||||
|
||||
|
||||
@property
|
||||
def utilisation(self):
|
||||
"""Getter for various Utilisation variables."""
|
||||
if self._utilisation is None:
|
||||
api = "SYNO.Core.System.Utilization"
|
||||
url = "%s/entry.cgi?api=%s&version=1&method=get" % (
|
||||
self.base_url,
|
||||
api)
|
||||
url = "%s/entry.cgi?api=%s&version=1&method=get" % (self.base_url, api)
|
||||
self._utilisation = SynoCoreUtilization(self._get_url(url))
|
||||
return self._utilisation
|
||||
|
||||
|
||||
@property
|
||||
def storage(self):
|
||||
"""Getter for various Storage variables."""
|
||||
@ -231,7 +240,8 @@ class SynologyDSM(object):
|
||||
api = "SYNO.Storage.CGI.Storage"
|
||||
url = "%s/entry.cgi?api=%s&version=1&method=load_info" % (
|
||||
self.base_url,
|
||||
api)
|
||||
api,
|
||||
)
|
||||
else:
|
||||
url = "%s?action=load_info" % self.storage_url
|
||||
|
||||
|
@ -5,6 +5,7 @@ from SynologyDSM.helpers import SynoFormatHelper
|
||||
|
||||
class SynoCoreUtilization(object):
|
||||
"""Class containing Utilisation data."""
|
||||
|
||||
def __init__(self, raw_input):
|
||||
self._data = None
|
||||
self.update(raw_input)
|
||||
@ -39,9 +40,7 @@ class SynoCoreUtilization(object):
|
||||
user_load = self.cpu_user_load
|
||||
other_load = self.cpu_other_load
|
||||
|
||||
if system_load is not None and \
|
||||
user_load is not None and \
|
||||
other_load is not None:
|
||||
if system_load is not None and user_load is not None and other_load is not None:
|
||||
return system_load + user_load + other_load
|
||||
|
||||
@property
|
||||
@ -74,8 +73,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["memory_size"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -85,8 +83,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["avail_swap"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -96,8 +93,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["cached"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -107,8 +103,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["avail_real"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -118,8 +113,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["total_real"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -129,8 +123,7 @@ class SynoCoreUtilization(object):
|
||||
# Memory is actually returned in KB's so multiply before converting
|
||||
return_data = int(self._data["memory"]["total_swap"]) * 1024
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -147,8 +140,7 @@ class SynoCoreUtilization(object):
|
||||
if network is not None:
|
||||
return_data = int(network["tx"])
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -158,7 +150,6 @@ class SynoCoreUtilization(object):
|
||||
if network is not None:
|
||||
return_data = int(network["rx"])
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
class SynoDSMInformation(object):
|
||||
"""Class containing Information data."""
|
||||
|
||||
def __init__(self, raw_input):
|
||||
self._data = None
|
||||
self.update(raw_input)
|
||||
|
@ -5,6 +5,7 @@ from SynologyDSM.helpers import SynoFormatHelper
|
||||
|
||||
class SynoStorage(object):
|
||||
"""Class containing Storage data."""
|
||||
|
||||
def __init__(self, raw_input):
|
||||
self._data = None
|
||||
self.update(raw_input)
|
||||
@ -48,8 +49,7 @@ class SynoStorage(object):
|
||||
if volume is not None:
|
||||
return_data = int(volume["size"]["total"])
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -59,8 +59,7 @@ class SynoStorage(object):
|
||||
if volume is not None:
|
||||
return_data = int(volume["size"]["used"])
|
||||
if human_readable:
|
||||
return SynoFormatHelper.bytes_to_readable(
|
||||
return_data)
|
||||
return SynoFormatHelper.bytes_to_readable(return_data)
|
||||
else:
|
||||
return return_data
|
||||
|
||||
@ -71,8 +70,7 @@ class SynoStorage(object):
|
||||
total = int(volume["size"]["total"])
|
||||
used = int(volume["size"]["used"])
|
||||
|
||||
if used is not None and used > 0 and \
|
||||
total is not None and total > 0:
|
||||
if used is not None and used > 0 and total is not None and total > 0:
|
||||
return round((float(used) / float(total)) * 100.0, 1)
|
||||
|
||||
def volume_disk_temp_avg(self, volume):
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Format Helper."""
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
class SynoFormatHelper(object):
|
||||
"""Class containing various formatting functions."""
|
||||
|
||||
@ -12,11 +13,11 @@ class SynoFormatHelper(object):
|
||||
elif num < 1024:
|
||||
return "1 Kb"
|
||||
|
||||
for unit in ['', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb']:
|
||||
for unit in ["", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb"]:
|
||||
if abs(num) < 1024.0:
|
||||
return "%3.1f%s" % (num, unit)
|
||||
num /= 1024.0
|
||||
return "%.1f%s" % (num, 'Yb')
|
||||
return "%.1f%s" % (num, "Yb")
|
||||
|
||||
@staticmethod
|
||||
def bytes_to_megabytes(num):
|
||||
|
16
scripts/check_format.sh
Executable file
16
scripts/check_format.sh
Executable file
@ -0,0 +1,16 @@
|
||||
./scripts/common.sh
|
||||
|
||||
if ! hash python3; then
|
||||
echo "python3 is not installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ver=$(python3 -V 2>&1 | sed 's/.* \([0-9]\).\([0-9]\).*/\1\2/')
|
||||
if [ "$ver" -lt "36" ]; then
|
||||
echo "This script requires python 3.6 or greater"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pip install black==19.10b0
|
||||
|
||||
black --check --fast .
|
7
scripts/clean.sh
Executable file
7
scripts/clean.sh
Executable file
@ -0,0 +1,7 @@
|
||||
./scripts/common.sh
|
||||
|
||||
# Clean
|
||||
rm -r .tox
|
||||
rm -r build
|
||||
rm -r dist
|
||||
rm -r python_synology.egg-info
|
4
scripts/common.sh
Executable file
4
scripts/common.sh
Executable file
@ -0,0 +1,4 @@
|
||||
# Be in right place
|
||||
if [ ! -f setup.py ]; then
|
||||
cd ..
|
||||
fi
|
22
setup.py
22
setup.py
@ -13,20 +13,20 @@ from os import path
|
||||
here = path.abspath(path.dirname(__file__))
|
||||
|
||||
# Get the long description from the README file
|
||||
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
|
||||
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name = 'python-synology',
|
||||
version = '0.4.0',
|
||||
url = 'https://github.com/StaticCube/python-synology/',
|
||||
download_url = 'https://github.com/StaticCube/python-synology/tarball/0.4.0',
|
||||
description = 'Python API for communication with Synology DSM',
|
||||
name="python-synology",
|
||||
version="0.4.0",
|
||||
url="https://github.com/StaticCube/python-synology/",
|
||||
download_url="https://github.com/StaticCube/python-synology/tarball/0.4.0",
|
||||
description="Python API for communication with Synology DSM",
|
||||
long_description=long_description,
|
||||
author = 'FG van Zeelst (StaticCube)',
|
||||
author_email = 'GitHub@StaticCube.com',
|
||||
packages = ['SynologyDSM'], # this must be the same as the name above
|
||||
install_requires=['requests>=1.0.0'],
|
||||
author="FG van Zeelst (StaticCube)",
|
||||
author_email="GitHub@StaticCube.com",
|
||||
packages=["SynologyDSM"], # this must be the same as the name above
|
||||
install_requires=["requests>=1.0.0"],
|
||||
python_requires=">=2.7.0",
|
||||
license="MIT",
|
||||
classifiers=[
|
||||
@ -40,5 +40,5 @@ setup(
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
],
|
||||
keywords = ['synology-dsm', 'synology'],
|
||||
keywords=["synology-dsm", "synology"],
|
||||
)
|
||||
|
Reference in New Issue
Block a user