Files
reolink_api/APIHandler.py
2019-08-11 13:45:51 +02:00

416 lines
15 KiB
Python

import io
import json
import random
import string
from urllib.request import urlopen
from PIL import Image
from resthandle import Request
class APIHandler:
"""
The APIHandler class is the backend part of the API.
This handles communication directly with the camera.
All Code will try to follow the PEP 8 standard as described here: https://www.python.org/dev/peps/pep-0008/
"""
def __init__(self, ip: str, username: str, password: str):
"""
Initialise the Camera API Handler (maps api calls into python)
:param ip:
:param username:
:param password:
"""
self.url = "http://" + ip + "/cgi-bin/api.cgi"
self.token = None
self.username = username
self.password = password
# Token
def login(self) -> bool:
"""
Get login token
Must be called first, before any other operation can be performed
:return: bool
"""
try:
body = [{"cmd": "Login", "action": 0,
"param": {"User": {"userName": self.username, "password": self.password}}}]
param = {"cmd": "Login", "token": "null"}
response = Request.post(self.url, data=body, params=param)
if response is not None:
data = json.loads(response.text)[0]
code = data["code"]
if int(code) == 0:
self.token = data["value"]["Token"]["name"]
print("Login success")
return True
print(self.token)
return False
else:
print("Failed to login\nStatus Code:", response.status_code)
return False
except Exception as e:
print("Error Login\n", e)
raise
###########
# NETWORK
###########
###########
# SET Network
###########
def set_net_port(self, http_port=80, https_port=443, media_port=9000, onvif_port=8000, rtmp_port=1935,
rtsp_port=554) -> bool:
"""
Set network ports
If nothing is specified, the default values will be used
:param rtsp_port: int
:param rtmp_port: int
:param onvif_port: int
:param media_port: int
:param https_port: int
:type http_port: int
:return: bool
"""
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "SetNetPort", "action": 0, "param": {"NetPort": {
"httpPort": http_port,
"httpsPort": https_port,
"mediaPort": media_port,
"onvifPort": onvif_port,
"rtmpPort": rtmp_port,
"rtspPort": rtsp_port
}}}]
param = {"token": self.token}
response = Request.post(self.url, data=body, params=param)
if response is not None:
if response.status_code == 200:
print("Successfully Set Network Ports")
return True
else:
print("Something went wront\nStatus Code:", response.status_code)
return False
return False
except Exception as e:
print("Setting Network Port Error\n", e)
raise
def set_wifi(self, ssid, password) -> json or None:
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "SetWifi", "action": 0, "param": {
"Wifi": {
"ssid": ssid,
"password": password
}}}]
param = {"cmd": "SetWifi", "token": self.token}
response = Request.post(self.url, data=body, params=param)
return json.loads(response.text)
except Exception as e:
print("Could not Set Wifi details", e)
raise
###########
# GET
###########
def get_net_ports(self):
"""
Get network ports
:return:
"""
try:
if self.token is not None:
raise ValueError("Login first")
body = [{"cmd": "GetNetPort", "action": 1, "param": {}},
{"cmd": "GetUpnp", "action": 0, "param": {}},
{"cmd": "GetP2p", "action": 0, "param": {}}]
param = {"token": self.token}
response = Request.post(self.url, data=body, params=param)
return json.loads(response.text)
except Exception as e:
print("Get Network Ports", e)
def get_link_local(self):
"""
Get General network data
This includes IP address, Device mac, Gateway and DNS
:return:
"""
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "GetLocalLink", "action": 1, "param": {}}]
param = {"cmd": "GetLocalLink", "token": self.token}
request = Request.post(self.url, data=body, params=param)
return json.loads(request.text)
except Exception as e:
print("Could not get Link Local", e)
raise
def get_wifi(self):
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "GetWifi", "action": 1, "param": {}}]
param = {"cmd": "GetWifi", "token": self.token}
response = Request.post(self.url, data=body, params=param)
return json.loads(response.text)
except Exception as e:
print("Could not get Wifi\n", e)
raise
def scan_wifi(self):
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "ScanWifi", "action": 1, "param": {}}]
param = {"cmd": "ScanWifi", "token": self.token}
response = Request.post(self.url, data=body, params=param)
return json.loads(response.text)
except Exception as e:
print("Could not Scan wifi\n", e)
raise
###########
# SYSTEM
###########
###########
# GET
###########
def get_general_system(self) -> json or None:
try:
if self.token is None:
raise ValueError("Login first")
body = [{"cmd": "GetTime", "action": 1, "param": {}}, {"cmd": "GetNorm", "action": 1, "param": {}}]
param = {"token": self.token}
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
return json.loads(response.text)
print("Could not retrieve general information from camera successfully. Status:", response.status_code)
return None
except Exception as e:
print("Could not get General System settings\n", e)
raise
def get_osd(self) -> json or None:
try:
param = {"cmd": "GetOsd", "token": self.token}
body = [{"cmd": "GetOsd", "action": 1, "param": {"channel": 0}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
return json.loads(response.text)
print("Could not retrieve OSD from camera successfully. Status:", response.status_code)
return None
except Exception as e:
print("Could not get OSD", e)
raise
##########
# User
##########
##########
# GET
##########
def get_online_user(self) -> json or None:
"""
Return a list of current logged-in users in json format
:return: json or None
"""
try:
param = {"cmd": "GetOnline", "token": self.token}
body = [{"cmd": "GetOnline", "action": 1, "param": {}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
return json.loads(response.text)
print("Could not retrieve online user from camera. Status:", response.status_code)
return None
except Exception as e:
print("Could not get online user", e)
raise
def get_users(self) -> json or None:
"""
Return a list of user accounts from the camera in json format
:return: json or None
"""
try:
param = {"cmd": "GetUser", "token": self.token}
body = [{"cmd": "GetUser", "action": 1, "param": {}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
return json.loads(response.text)
print("Could not retrieve users from camera. Status:", response.status_code)
return None
except Exception as e:
print("Could not get users", e)
raise
##########
# SET
##########
def add_user(self, username: str, password: str, level: str = "guest") -> bool:
"""
Add a new user account to the camera
:param username: The user's username
:param password: The user's password
:param level: The privilege level 'guest' or 'admin'. Default is 'guest'
:return: bool
"""
try:
param = {"cmd": "AddUser", "token": self.token}
body = [{"cmd": "AddUser", "action": 0,
"param": {"User": {"userName": username, "password": password, "level": level}}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
r_data = json.loads(response.text)
if r_data["value"]["rspCode"] == "200":
return True
print("Could not add user. Camera responded with:", r_data["value"])
return False
print("Something went wrong. Could not add user. Status:", response.status_code)
return False
except Exception as e:
print("Could not add user", e)
raise
def modify_user(self, username: str, password: str) -> bool:
"""
Modify the user's password by specifying their username
:param username: The user which would want to be modified
:param password: The new password
:return: bool
"""
try:
param = {"cmd": "ModifyUser", "token": self.token}
body = [{"cmd": "ModifyUser", "action": 0, "param": {"User": {"userName": username, "password": password}}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
r_data = json.loads(response.text)
if r_data["value"]["rspCode"] == "200":
return True
print("Could not modify user:", username, "\nCamera responded with:", r_data["value"])
print("Something went wrong. Could not modify user. Status:", response.status_code)
return False
except Exception as e:
print("Could not modify user", e)
raise
def delete_user(self, username: str) -> bool:
"""
Delete a user by specifying their username
:param username: The user which would want to be deleted
:return: bool
"""
try:
param = {"cmd": "DelUser", "token": self.token}
body = [{"cmd": "DelUser", "action": 0, "param": {"User": {"userName": username}}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
r_data = json.loads(response.text)
if r_data["value"]["rspCode"] == "200":
return True
print("Could not delete user:", username, "\nCamera responded with:", r_data["value"])
return False
except Exception as e:
print("Could not delete user", e)
raise
##########
# Image Data
##########
def get_snap(self, timeout: int = 3) -> Image or None:
"""
Gets a "snap" of the current camera video data and returns a Pillow Image or None
:param timeout: Request timeout to camera in seconds
:return: Image or None
"""
try:
randomstr = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
snap = "?cmd=Snap&channel=0&rs=" \
+ randomstr \
+ "&user=" + self.username \
+ "&password=" + self.password
reader = urlopen(self.url + snap, timeout)
if reader.status == 200:
b = bytearray(reader.read())
return Image.open(io.BytesIO(b))
print("Could not retrieve data from camera successfully. Status:", reader.status)
return None
except Exception as e:
print("Could not get Image data\n", e)
raise
#########
# Device
#########
def get_hdd_info(self) -> json or None:
"""
Gets all HDD and SD card information from Camera
Format is as follows:
[{"cmd" : "GetHddInfo",
"code" : 0,
"value" : {
"HddInfo" : [{
"capacity" : 15181,
"format" : 1,
"id" : 0,
"mount" : 1,
"size" : 15181
}]
}
}]
:return: json or None
"""
try:
param = {"cmd": "GetHddInfo", "token": self.token}
body = [{"cmd": "GetHddInfo", "action": 0, "param": {}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
return json.loads(response.text)
print("Could not retrieve HDD/SD info from camera successfully. Status:", response.status_code)
return None
except Exception as e:
print("Could not get HDD/SD card information", e)
raise
def format_hdd(self, hdd_id: [int] = [0]) -> bool:
"""
Format specified HDD/SD cards with their id's
:param hdd_id: List of id's specified by the camera with get_hdd_info api. Default is 0 (SD card)
:return: bool
"""
try:
param = {"cmd": "Format", "token": self.token}
body = [{"cmd": "Format", "action": 0, "param": {"HddInfo": {"id": hdd_id}}}]
response = Request.post(self.url, data=body, params=param)
if response.status_code == 200:
r_data = json.loads(response.text)
if r_data["value"]["rspCode"] == "200":
return True
print("Could not format HDD/SD. Camera responded with:", r_data["value"])
return False
print("Could not format HDD/SD. Status:", response.status_code)
return False
except Exception as e:
print("Could not format HDD/SD", e)
raise