From cc5fa25fd0404a8b08f9ee97458d38dc28e8f163 Mon Sep 17 00:00:00 2001 From: Christophe Painchaud Date: Wed, 6 Oct 2021 16:08:49 +0200 Subject: [PATCH 1/2] remove uneeded debug --- reolink/camera_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reolink/camera_api.py b/reolink/camera_api.py index 827e005..fd821a3 100644 --- a/reolink/camera_api.py +++ b/reolink/camera_api.py @@ -971,7 +971,6 @@ async def send_search( try: json_data = json.loads(response) - _LOGGER.debug("Response from %s: %s", self._host, json_data) except (TypeError, json.JSONDecodeError): _LOGGER.debug( "Host %s: Error translating %s response to json", self._host, command From 1274e6dc10433ef59d41fbc8f0e6a667da9fee8b Mon Sep 17 00:00:00 2001 From: Christophe Painchaud Date: Fri, 8 Oct 2021 14:23:39 +0200 Subject: [PATCH 2/2] support for HTTPS based API --- reolink/camera_api.py | 32 +++++++++++++++++++++++++------- reolink/subscription_manager.py | 14 +++++++++++--- setup.py | 2 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/reolink/camera_api.py b/reolink/camera_api.py index fd821a3..6b8b110 100644 --- a/reolink/camera_api.py +++ b/reolink/camera_api.py @@ -14,6 +14,7 @@ import urllib.parse as parse MANUFACTURER = "Reolink" +DEFAULT_USE_SSL = False DEFAULT_STREAM = "main" DEFAULT_PROTOCOL = "rtmp" DEFAULT_CHANNEL = 0 @@ -26,6 +27,7 @@ ref_sw_version_3_0_0_0_0 = SoftwareVersion("v3.0.0.0_0") +ref_sw_version_3_1_0_0_0 = SoftwareVersion("v3.1.0.0_0") class Api: # pylint: disable=too-many-instance-attributes disable=too-many-public-methods @@ -37,6 +39,7 @@ def __init__( port, username, password, + use_https=DEFAULT_USE_SSL, channel=DEFAULT_CHANNEL, protocol=DEFAULT_PROTOCOL, stream=DEFAULT_STREAM, @@ -45,7 +48,7 @@ def __init__( rtmp_auth_method=DEFAULT_RTMP_AUTH_METHOD, ): """Initialize the API class.""" - self._url = f"http://{host}:{port}/cgi-bin/api.cgi" + self._url = "" self._host = host self._port = port self._username = username @@ -56,6 +59,7 @@ def __init__( self._stream_format = stream_format self._rtmp_auth_method = rtmp_auth_method self._timeout = aiohttp.ClientTimeout(total=timeout) + self._use_https = use_https self._token = None self._lease_time = None @@ -103,6 +107,18 @@ def __init__( self._is_nvr = False + self.refresh_base_url() + + def enable_https(self, enable: bool): + self._use_https = enable + self.refresh_base_url() + + def refresh_base_url(self): + if self._use_https: + self._url = f"https://{self._host}:{self._port}/cgi-bin/api.cgi" + else: + self._url = f"http://{self._host}:{self._port}/cgi-bin/api.cgi" + @property def host(self): """Return the host.""" @@ -339,7 +355,7 @@ async def get_states(self, cmd_list=None): {"cmd": "GetPushV20", "action": 1, "param": {"channel": self._channel}}, ] - if not self._is_nvr: + if not self._is_nvr and self._sw_version_object < ref_sw_version_3_1_0_0_0: # NVR would crash without this body.append({"cmd": "GetPush", "action": 1, "param": {"channel": self._channel}}) @@ -993,7 +1009,7 @@ async def send_search( return search_result["Status"], search_result["File"] - _LOGGER.debug("Host: %s: Failed to get results for %s", self._host, command) + _LOGGER.warning("Host: %s: Failed to get results for %s, JSON data was was empty?", self._host, command) return None, None async def send_setting(self, body): @@ -1045,8 +1061,9 @@ async def send(self, body, param=None): try: if body is None: - async with aiohttp.ClientSession(timeout=self._timeout) as session: - async with session.get(url=self._url, params=param) as response: + async with aiohttp.ClientSession(timeout=self._timeout, + connector=aiohttp.TCPConnector(verify_ssl=False)) as session: + async with session.get(url=self._url, params=param, allow_redirects=False) as response: _LOGGER.debug("send()= HTTP Request params =%s", str(param).replace(self._password, "")) json_data = await response.read() _LOGGER.debug("send HTTP Response status=%s", str(response.status)) @@ -1057,9 +1074,10 @@ async def send(self, body, param=None): return json_data else: - async with aiohttp.ClientSession(timeout=self._timeout) as session: + async with aiohttp.ClientSession(timeout=self._timeout, + connector=aiohttp.TCPConnector(verify_ssl=False)) as session: async with session.post( - url=self._url, json=body, params=param + url=self._url, json=body, params=param, allow_redirects=False ) as response: _LOGGER.debug("send() HTTP Request params =%s", str(param).replace(self._password, "")) _LOGGER.debug("send() HTTP Request body =%s", str(body).replace(self._password, "")) diff --git a/reolink/subscription_manager.py b/reolink/subscription_manager.py index 439b8b2..c0b181c 100644 --- a/reolink/subscription_manager.py +++ b/reolink/subscription_manager.py @@ -81,18 +81,26 @@ async def send(self, headers, data): """Send data to the camera.""" try: - async with aiohttp.ClientSession(timeout=self._timeout) as session: + async with aiohttp.ClientSession(timeout=self._timeout, + connector=aiohttp.TCPConnector(verify_ssl=False)) as session: + _LOGGER.debug( + "Reolink host %s (Subscription) request data: %s", + self._host, data + ) + async with session.post( - url=self._subscribe_url, data=data, headers=headers + url=self._subscribe_url, data=data, headers=headers, allow_redirects=False ) as response: response_xml = await response.text() _LOGGER.debug( - "Reolink host %s got response status: %s. Payload: {%s}", + "Reolink host %s (Subscription) got response status: %s. Payload: %s", self._host, response.status, response_xml ) if response.status == 200: return response_xml + else: + _LOGGER.warning("Subscription process ended with wrong HTTP status: %s: %s", response.status, response.reason) return diff --git a/setup.py b/setup.py index 1592901..e3e4776 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='reolink', packages=['reolink'], - version='0.0.26', + version='0.0.27', license='MIT', description='Reolink camera package', author='fwestenberg',