mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Add stream_rtsp_url to camera module (#1197)
This commit is contained in:
parent
91e219f467
commit
1e0ca799bc
@ -2,10 +2,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from ...credentials import Credentials
|
||||
from ...device_type import DeviceType
|
||||
from ...feature import Feature
|
||||
from ..smartcameramodule import SmartCameraModule
|
||||
|
||||
LOCAL_STREAMING_PORT = 554
|
||||
|
||||
|
||||
class Camera(SmartCameraModule):
|
||||
"""Implementation of device module."""
|
||||
@ -31,11 +36,32 @@ class Camera(SmartCameraModule):
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the device id."""
|
||||
return self.data["lens_mask_info"]["enabled"] == "on"
|
||||
return self.data["lens_mask_info"]["enabled"] == "off"
|
||||
|
||||
def stream_rtsp_url(self, credentials: Credentials | None = None) -> str | None:
|
||||
"""Return the local rtsp streaming url.
|
||||
|
||||
:param credentials: Credentials for camera account.
|
||||
These could be different credentials to tplink cloud credentials.
|
||||
If not provided will use tplink credentials if available
|
||||
:return: rtsp url with escaped credentials or None if no credentials or
|
||||
camera is off.
|
||||
"""
|
||||
if not self.is_on:
|
||||
return None
|
||||
dev = self._device
|
||||
if not credentials:
|
||||
credentials = dev.credentials
|
||||
if not credentials or not credentials.username or not credentials.password:
|
||||
return None
|
||||
username = quote_plus(credentials.username)
|
||||
password = quote_plus(credentials.password)
|
||||
return f"rtsp://{username}:{password}@{dev.host}:{LOCAL_STREAMING_PORT}/stream1"
|
||||
|
||||
async def set_state(self, on: bool) -> dict:
|
||||
"""Set the device state."""
|
||||
params = {"enabled": "on" if on else "off"}
|
||||
# Turning off enables the privacy mask which is why value is reversed.
|
||||
params = {"enabled": "off" if on else "on"}
|
||||
return await self._device._query_setter_helper(
|
||||
"setLensMaskConfig", self.QUERY_MODULE_NAME, "lens_mask_info", params
|
||||
)
|
||||
|
@ -120,11 +120,13 @@ class SslAesTransport(BaseTransport):
|
||||
self._seq: int | None = None
|
||||
self._pwd_hash: str | None = None
|
||||
self._username: str | None = None
|
||||
self._password: str | None = None
|
||||
if self._credentials != Credentials() and self._credentials:
|
||||
self._username = self._credentials.username
|
||||
self._password = self._credentials.password
|
||||
elif self._credentials_hash:
|
||||
ch = json_loads(base64.b64decode(self._credentials_hash.encode()))
|
||||
self._pwd_hash = ch["pwd"]
|
||||
self._password = ch["pwd"]
|
||||
self._username = ch["un"]
|
||||
self._local_nonce: str | None = None
|
||||
|
||||
@ -140,10 +142,10 @@ class SslAesTransport(BaseTransport):
|
||||
"""The hashed credentials used by the transport."""
|
||||
if self._credentials == Credentials():
|
||||
return None
|
||||
if self._credentials_hash:
|
||||
if not self._credentials and self._credentials_hash:
|
||||
return self._credentials_hash
|
||||
if self._pwd_hash and self._credentials:
|
||||
ch = {"un": self._credentials.username, "pwd": self._pwd_hash}
|
||||
if (cred := self._credentials) and cred.password and cred.username:
|
||||
ch = {"un": cred.username, "pwd": cred.password}
|
||||
return base64.b64encode(json_dumps(ch).encode()).decode()
|
||||
return None
|
||||
|
||||
|
@ -3,13 +3,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from kasa import Device, DeviceType, Module
|
||||
from kasa import Credentials, Device, DeviceType, Module
|
||||
|
||||
from ..conftest import device_smartcamera, hub_smartcamera
|
||||
from ..conftest import camera_smartcamera, device_smartcamera, hub_smartcamera
|
||||
|
||||
|
||||
@device_smartcamera
|
||||
@ -23,6 +24,43 @@ async def test_state(dev: Device):
|
||||
assert dev.is_on is not state
|
||||
|
||||
|
||||
@camera_smartcamera
|
||||
async def test_stream_rtsp_url(dev: Device):
|
||||
camera_module = dev.modules.get(Module.Camera)
|
||||
assert camera_module
|
||||
|
||||
await camera_module.set_state(True)
|
||||
await dev.update()
|
||||
assert camera_module.is_on
|
||||
url = camera_module.stream_rtsp_url(Credentials("foo", "bar"))
|
||||
assert url == "rtsp://foo:bar@127.0.0.123:554/stream1"
|
||||
|
||||
with patch.object(
|
||||
dev.protocol._transport, "_credentials", Credentials("bar", "foo")
|
||||
):
|
||||
url = camera_module.stream_rtsp_url()
|
||||
assert url == "rtsp://bar:foo@127.0.0.123:554/stream1"
|
||||
|
||||
with patch.object(dev.protocol._transport, "_credentials", Credentials("bar", "")):
|
||||
url = camera_module.stream_rtsp_url()
|
||||
assert url is None
|
||||
|
||||
with patch.object(dev.protocol._transport, "_credentials", Credentials("", "Foo")):
|
||||
url = camera_module.stream_rtsp_url()
|
||||
assert url is None
|
||||
|
||||
# Test with camera off
|
||||
await camera_module.set_state(False)
|
||||
await dev.update()
|
||||
url = camera_module.stream_rtsp_url(Credentials("foo", "bar"))
|
||||
assert url is None
|
||||
with patch.object(
|
||||
dev.protocol._transport, "_credentials", Credentials("bar", "foo")
|
||||
):
|
||||
url = camera_module.stream_rtsp_url()
|
||||
assert url is None
|
||||
|
||||
|
||||
@device_smartcamera
|
||||
async def test_alias(dev):
|
||||
test_alias = "TEST1234"
|
||||
|
Loading…
Reference in New Issue
Block a user