mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
Add LensMask module to smartcam (#1385)
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run
Ensures no error with devices that do not have the `lens_mask` component.
This commit is contained in:
parent
37ef7b0463
commit
ba273f308e
@ -159,6 +159,7 @@ class Module(ABC):
|
|||||||
|
|
||||||
# SMARTCAM only modules
|
# SMARTCAM only modules
|
||||||
Camera: Final[ModuleName[smartcam.Camera]] = ModuleName("Camera")
|
Camera: Final[ModuleName[smartcam.Camera]] = ModuleName("Camera")
|
||||||
|
LensMask: Final[ModuleName[smartcam.LensMask]] = ModuleName("LensMask")
|
||||||
|
|
||||||
def __init__(self, device: Device, module: str) -> None:
|
def __init__(self, device: Device, module: str) -> None:
|
||||||
self._device = device
|
self._device = device
|
||||||
|
@ -6,6 +6,7 @@ from .childdevice import ChildDevice
|
|||||||
from .device import DeviceModule
|
from .device import DeviceModule
|
||||||
from .homekit import HomeKit
|
from .homekit import HomeKit
|
||||||
from .led import Led
|
from .led import Led
|
||||||
|
from .lensmask import LensMask
|
||||||
from .matter import Matter
|
from .matter import Matter
|
||||||
from .pantilt import PanTilt
|
from .pantilt import PanTilt
|
||||||
from .time import Time
|
from .time import Time
|
||||||
@ -20,4 +21,5 @@ __all__ = [
|
|||||||
"Time",
|
"Time",
|
||||||
"HomeKit",
|
"HomeKit",
|
||||||
"Matter",
|
"Matter",
|
||||||
|
"LensMask",
|
||||||
]
|
]
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
"""Implementation of device module."""
|
"""Implementation of camera module."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
|
from typing import Annotated
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
from ...credentials import Credentials
|
from ...credentials import Credentials
|
||||||
from ...device_type import DeviceType
|
from ...device_type import DeviceType
|
||||||
from ...feature import Feature
|
from ...feature import Feature
|
||||||
from ...json import loads as json_loads
|
from ...json import loads as json_loads
|
||||||
|
from ...module import FeatureAttribute, Module
|
||||||
from ..smartcammodule import SmartCamModule
|
from ..smartcammodule import SmartCamModule
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -29,12 +31,9 @@ class StreamResolution(StrEnum):
|
|||||||
class Camera(SmartCamModule):
|
class Camera(SmartCamModule):
|
||||||
"""Implementation of device module."""
|
"""Implementation of device module."""
|
||||||
|
|
||||||
QUERY_GETTER_NAME = "getLensMaskConfig"
|
|
||||||
QUERY_MODULE_NAME = "lens_mask"
|
|
||||||
QUERY_SECTION_NAMES = "lens_mask_info"
|
|
||||||
|
|
||||||
def _initialize_features(self) -> None:
|
def _initialize_features(self) -> None:
|
||||||
"""Initialize features after the initial update."""
|
"""Initialize features after the initial update."""
|
||||||
|
if Module.LensMask in self._device.modules:
|
||||||
self._add_feature(
|
self._add_feature(
|
||||||
Feature(
|
Feature(
|
||||||
self._device,
|
self._device,
|
||||||
@ -49,8 +48,20 @@ class Camera(SmartCamModule):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return the device id."""
|
"""Return the device on state."""
|
||||||
return self.data["lens_mask_info"]["enabled"] == "off"
|
if lens_mask := self._device.modules.get(Module.LensMask):
|
||||||
|
return lens_mask.state
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def set_state(self, on: bool) -> Annotated[dict, FeatureAttribute()]:
|
||||||
|
"""Set the device on state.
|
||||||
|
|
||||||
|
If the device does not support setting state will do nothing.
|
||||||
|
"""
|
||||||
|
if lens_mask := self._device.modules.get(Module.LensMask):
|
||||||
|
# Turning off enables the privacy mask which is why value is reversed.
|
||||||
|
return await lens_mask.set_state(not on)
|
||||||
|
return {}
|
||||||
|
|
||||||
def _get_credentials(self) -> Credentials | None:
|
def _get_credentials(self) -> Credentials | None:
|
||||||
"""Get credentials from ."""
|
"""Get credentials from ."""
|
||||||
@ -109,14 +120,6 @@ class Camera(SmartCamModule):
|
|||||||
"""Return the onvif url."""
|
"""Return the onvif url."""
|
||||||
return f"http://{self._device.host}:{ONVIF_PORT}/onvif/device_service"
|
return f"http://{self._device.host}:{ONVIF_PORT}/onvif/device_service"
|
||||||
|
|
||||||
async def set_state(self, on: bool) -> dict:
|
|
||||||
"""Set the device state."""
|
|
||||||
# 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
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _check_supported(self) -> bool:
|
async def _check_supported(self) -> bool:
|
||||||
"""Additional check to see if the module is supported by the device."""
|
"""Additional check to see if the module is supported by the device."""
|
||||||
return self._device.device_type is DeviceType.Camera
|
return self._device.device_type is DeviceType.Camera
|
||||||
|
29
kasa/smartcam/modules/lensmask.py
Normal file
29
kasa/smartcam/modules/lensmask.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""Implementation of lens mask privacy module."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ..smartcammodule import SmartCamModule
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class LensMask(SmartCamModule):
|
||||||
|
"""Implementation of lens mask module."""
|
||||||
|
|
||||||
|
QUERY_GETTER_NAME = "getLensMaskConfig"
|
||||||
|
QUERY_MODULE_NAME = "lens_mask"
|
||||||
|
QUERY_SECTION_NAMES = "lens_mask_info"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> bool:
|
||||||
|
"""Return the lens mask state."""
|
||||||
|
return self.data["lens_mask_info"]["enabled"] == "off"
|
||||||
|
|
||||||
|
async def set_state(self, state: bool) -> dict:
|
||||||
|
"""Set the lens mask state."""
|
||||||
|
params = {"enabled": "on" if state else "off"}
|
||||||
|
return await self._device._query_setter_helper(
|
||||||
|
"setLensMaskConfig", self.QUERY_MODULE_NAME, "lens_mask_info", params
|
||||||
|
)
|
@ -134,6 +134,11 @@ class SmartCamDevice(SmartDevice):
|
|||||||
if (
|
if (
|
||||||
mod.REQUIRED_COMPONENT
|
mod.REQUIRED_COMPONENT
|
||||||
and mod.REQUIRED_COMPONENT not in self._components
|
and mod.REQUIRED_COMPONENT not in self._components
|
||||||
|
# Always add Camera module to cameras
|
||||||
|
and (
|
||||||
|
mod._module_name() != Module.Camera
|
||||||
|
or self._device_type is not DeviceType.Camera
|
||||||
|
)
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
module = mod(self, mod._module_name())
|
module = mod(self, mod._module_name())
|
||||||
|
@ -17,11 +17,21 @@ async def test_state(dev: Device):
|
|||||||
if dev.device_type is DeviceType.Hub:
|
if dev.device_type is DeviceType.Hub:
|
||||||
pytest.skip("Hubs cannot be switched on and off")
|
pytest.skip("Hubs cannot be switched on and off")
|
||||||
|
|
||||||
|
if Module.LensMask in dev.modules:
|
||||||
state = dev.is_on
|
state = dev.is_on
|
||||||
await dev.set_state(not state)
|
await dev.set_state(not state)
|
||||||
await dev.update()
|
await dev.update()
|
||||||
assert dev.is_on is not state
|
assert dev.is_on is not state
|
||||||
|
|
||||||
|
dev.modules.pop(Module.LensMask) # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
# Test with no lens mask module. Device is always on.
|
||||||
|
assert dev.is_on is True
|
||||||
|
res = await dev.set_state(False)
|
||||||
|
assert res == {}
|
||||||
|
await dev.update()
|
||||||
|
assert dev.is_on is True
|
||||||
|
|
||||||
|
|
||||||
@device_smartcam
|
@device_smartcam
|
||||||
async def test_alias(dev):
|
async def test_alias(dev):
|
||||||
|
Loading…
Reference in New Issue
Block a user