Add alarm module for smartcamera hubs (#1258)

This commit is contained in:
Steven B.
2024-11-15 10:19:40 +00:00
committed by GitHub
parent 5fe75cada9
commit cf77128853
6 changed files with 372 additions and 43 deletions

View File

@@ -105,6 +105,7 @@ class FakeSmartCameraTransport(BaseTransport):
info = info[key]
info[set_keys[-1]] = value
# Setters for when there's not a simple mapping of setters to getters
SETTERS = {
("system", "sys", "dev_alias"): [
"getDeviceInfo",
@@ -112,36 +113,20 @@ class FakeSmartCameraTransport(BaseTransport):
"basic_info",
"device_alias",
],
("lens_mask", "lens_mask_info", "enabled"): [
"getLensMaskConfig",
"lens_mask",
"lens_mask_info",
"enabled",
],
# setTimezone maps to getClockStatus
("system", "clock_status", "seconds_from_1970"): [
"getClockStatus",
"system",
"clock_status",
"seconds_from_1970",
],
# setTimezone maps to getClockStatus
("system", "clock_status", "local_time"): [
"getClockStatus",
"system",
"clock_status",
"local_time",
],
("system", "basic", "zone_id"): [
"getTimezone",
"system",
"basic",
"zone_id",
],
("led", "config", "enabled"): [
"getLedStatus",
"led",
"config",
"enabled",
],
}
async def _send_request(self, request_dict: dict):
@@ -154,27 +139,41 @@ class FakeSmartCameraTransport(BaseTransport):
)
if method[:3] == "set":
get_method = "g" + method[1:]
for key, val in request_dict.items():
if key != "method":
# key is params for multi request and the actual params
# for single requests
if key == "params":
module = next(iter(val))
val = val[module]
if key == "method":
continue
# key is params for multi request and the actual params
# for single requests
if key == "params":
module = next(iter(val))
val = val[module]
else:
module = key
section = next(iter(val))
skey_val = val[section]
if not isinstance(skey_val, dict): # single level query
section_key = section
section_val = skey_val
if (get_info := info.get(get_method)) and section_key in get_info:
get_info[section_key] = section_val
else:
module = key
section = next(iter(val))
skey_val = val[section]
for skey, sval in skey_val.items():
section_key = skey
section_value = sval
if setter_keys := self.SETTERS.get(
(module, section, section_key)
):
self._get_param_set_value(info, setter_keys, section_value)
else:
return {"error_code": -1}
return {"error_code": -1}
break
for skey, sval in skey_val.items():
section_key = skey
section_value = sval
if setter_keys := self.SETTERS.get((module, section, section_key)):
self._get_param_set_value(info, setter_keys, section_value)
elif (
section := info.get(get_method, {})
.get(module, {})
.get(section, {})
) and section_key in section:
section[section_key] = section_value
else:
return {"error_code": -1}
break
return {"error_code": 0}
elif method[:3] == "get":
params = request_dict.get("params")

View File

View File

@@ -0,0 +1,159 @@
"""Tests for smart camera devices."""
from __future__ import annotations
import pytest
from kasa import Device
from kasa.smartcamera.modules.alarm import (
DURATION_MAX,
DURATION_MIN,
VOLUME_MAX,
VOLUME_MIN,
)
from kasa.smartcamera.smartcameramodule import SmartCameraModule
from ...conftest import hub_smartcamera
@hub_smartcamera
async def test_alarm(dev: Device):
"""Test device alarm."""
alarm = dev.modules.get(SmartCameraModule.SmartCameraAlarm)
assert alarm
original_duration = alarm.alarm_duration
assert original_duration is not None
original_volume = alarm.alarm_volume
assert original_volume is not None
original_sound = alarm.alarm_sound
try:
# test volume
new_volume = original_volume - 1 if original_volume > 1 else original_volume + 1
await alarm.set_alarm_volume(new_volume) # type: ignore[arg-type]
await dev.update()
assert alarm.alarm_volume == new_volume
# test duration
new_duration = (
original_duration - 1 if original_duration > 1 else original_duration + 1
)
await alarm.set_alarm_duration(new_duration)
await dev.update()
assert alarm.alarm_duration == new_duration
# test start
await alarm.play()
await dev.update()
assert alarm.active
# test stop
await alarm.stop()
await dev.update()
assert not alarm.active
# test set sound
new_sound = (
alarm.alarm_sounds[0]
if alarm.alarm_sound != alarm.alarm_sounds[0]
else alarm.alarm_sounds[1]
)
await alarm.set_alarm_sound(new_sound)
await dev.update()
assert alarm.alarm_sound == new_sound
finally:
await alarm.set_alarm_volume(original_volume)
await alarm.set_alarm_duration(original_duration)
await alarm.set_alarm_sound(original_sound)
await dev.update()
@hub_smartcamera
async def test_alarm_invalid_setters(dev: Device):
"""Test device alarm invalid setter values."""
alarm = dev.modules.get(SmartCameraModule.SmartCameraAlarm)
assert alarm
# test set sound invalid
msg = f"sound must be one of {', '.join(alarm.alarm_sounds)}: foobar"
with pytest.raises(ValueError, match=msg):
await alarm.set_alarm_sound("foobar")
# test volume invalid
msg = f"volume must be between {VOLUME_MIN} and {VOLUME_MAX}"
with pytest.raises(ValueError, match=msg):
await alarm.set_alarm_volume(-3)
# test duration invalid
msg = f"duration must be between {DURATION_MIN} and {DURATION_MAX}"
with pytest.raises(ValueError, match=msg):
await alarm.set_alarm_duration(-3)
@hub_smartcamera
async def test_alarm_features(dev: Device):
"""Test device alarm features."""
alarm = dev.modules.get(SmartCameraModule.SmartCameraAlarm)
assert alarm
original_duration = alarm.alarm_duration
assert original_duration is not None
original_volume = alarm.alarm_volume
assert original_volume is not None
original_sound = alarm.alarm_sound
try:
# test volume
new_volume = original_volume - 1 if original_volume > 1 else original_volume + 1
feature = dev.features.get("alarm_volume")
assert feature
await feature.set_value(new_volume) # type: ignore[arg-type]
await dev.update()
assert feature.value == new_volume
# test duration
feature = dev.features.get("alarm_duration")
assert feature
new_duration = (
original_duration - 1 if original_duration > 1 else original_duration + 1
)
await feature.set_value(new_duration)
await dev.update()
assert feature.value == new_duration
# test start
feature = dev.features.get("test_alarm")
assert feature
await feature.set_value(None)
await dev.update()
feature = dev.features.get("alarm")
assert feature
assert feature.value is True
# test stop
feature = dev.features.get("stop_alarm")
assert feature
await feature.set_value(None)
await dev.update()
assert dev.features["alarm"].value is False
# test set sound
feature = dev.features.get("alarm_sound")
assert feature
new_sound = (
alarm.alarm_sounds[0]
if alarm.alarm_sound != alarm.alarm_sounds[0]
else alarm.alarm_sounds[1]
)
await feature.set_value(new_sound)
await alarm.set_alarm_sound(new_sound)
await dev.update()
assert feature.value == new_sound
finally:
await alarm.set_alarm_volume(original_volume)
await alarm.set_alarm_duration(original_duration)
await alarm.set_alarm_sound(original_sound)
await dev.update()