Skip to content

Initial support for vacuums (clean module) #944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2995ee2
Initial support for vacuums (clean module)
rytilahti Jun 2, 2024
d61eedd
Await calls
rytilahti Jun 2, 2024
6969609
Cleanup a bit
rytilahti Nov 30, 2024
7f3832e
Fix ids and names
rytilahti Nov 30, 2024
42fb9a3
Add battery level
rytilahti Nov 30, 2024
8f095e2
Add vacuum module to Module
rytilahti Nov 30, 2024
0c0af9f
Add fan speed setting
rytilahti Dec 2, 2024
856bbc0
Add .HTTPS to the lookup key
rytilahti Jan 3, 2025
ef1d47d
Debug log httpclient response
rytilahti Jan 3, 2025
0f54d2a
Revert "Debug log httpclient response"
rytilahti Jan 3, 2025
168862c
Fix fan_speed feature getter+setter
rytilahti Jan 3, 2025
31272d1
Use fanspeed keys as choices
rytilahti Jan 3, 2025
5b8ab3d
Fix error handling
rytilahti Jan 3, 2025
dc2e1e9
Fix error code naming
rytilahti Jan 3, 2025
c6d99af
Don't lowercase fan speed preset names
rytilahti Jan 3, 2025
d0739ac
Log a debug message on unknown error codes
rytilahti Jan 3, 2025
141960c
Add some more error codes
rytilahti Jan 3, 2025
6082107
Log a warning instead of debug message for unknown errors
rytilahti Jan 3, 2025
2e55a78
Add undocked state
rytilahti Jan 6, 2025
b8cf1eb
Remove Undocked from errorcode
rytilahti Jan 6, 2025
b8415e5
Fix get_protocol debug message
rytilahti Jan 11, 2025
a001255
Add RV20 Max Plus fixture
rytilahti Jan 11, 2025
cd25ed9
Add featureattribute annotation for fan_speed_preset
rytilahti Jan 11, 2025
1ede70a
rename vacuum to clean
rytilahti Jan 11, 2025
160c618
Add property getter for settings
rytilahti Jan 11, 2025
7c27f99
Add initial tests
rytilahti Jan 11, 2025
4c8a5b9
cleanup
rytilahti Jan 12, 2025
2fe2c0e
add tests for actions
rytilahti Jan 12, 2025
76390aa
Use get/set prefix in FakeSmartTransport instead of get_/set_
sdb9696 Jan 12, 2025
65a3bac
Handle setter-only methods
rytilahti Jan 12, 2025
fdba153
Fix docstring
rytilahti Jan 12, 2025
b77b908
dump_devinfo: add more vac requests
rytilahti Jan 11, 2025
1040922
Remove getMapData for now
rytilahti Jan 12, 2025
de22d8b
Fix tests
rytilahti Jan 13, 2025
368e545
Read default port from discovery response
rytilahti Jan 13, 2025
7947be6
Improve coverage
rytilahti Jan 13, 2025
a4223ec
Wrap the battery-too-low exception when trying to start cleaning
rytilahti Jan 13, 2025
049731f
Use add VACUUM_BATTERY_LOW exception
rytilahti Jan 13, 2025
c596675
Merge remote-tracking branch 'upstream/master' into feat/vacuum
rytilahti Jan 13, 2025
e750348
Add SideBrushStuck error
rytilahti Jan 13, 2025
622b3b6
Add battery low error
rytilahti Jan 13, 2025
6f2ecd6
Reduce logging verbosity by removing config logging for try_connect_all
rytilahti Jan 13, 2025
0e6fb77
Merge remote-tracking branch 'upstream/master' into feat/vacuum
sdb9696 Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ The following devices have been tested and confirmed as working. If your device
- **Cameras**: C100, C210, C225, C325WB, C520WS, C720, D230, TC65, TC70
- **Hubs**: H100, H200
- **Hub-Connected Devices[^3]**: S200B, S200D, T100, T110, T300, T310, T315
- **Vacuums**: RV20 Max Plus

<!--SUPPORTED_END-->
[^1]: Model requires authentication
Expand Down
5 changes: 5 additions & 0 deletions SUPPORTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
- Hardware: 1.0 (EU) / Firmware: 1.7.0
- Hardware: 1.0 (US) / Firmware: 1.8.0

### Vacuums

- **RV20 Max Plus**
- Hardware: 1.0 (EU) / Firmware: 1.0.7


<!--SUPPORTED_END-->
[^1]: Model requires authentication
1 change: 1 addition & 0 deletions devtools/generate_supported.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SupportedVersion(NamedTuple):
DeviceType.Hub: "Hubs",
DeviceType.Sensor: "Hub-Connected Devices",
DeviceType.Thermostat: "Hub-Connected Devices",
DeviceType.Vacuum: "Vacuums",
}


Expand Down
12 changes: 12 additions & 0 deletions devtools/helpers/smartrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ class DynamicLightEffectParams(SmartRequestParams):
enable: bool
id: str | None = None

@dataclass
class GetCleanAttrParams(SmartRequestParams):
"""CleanAttr params.

Decides which cleaning settings are requested
"""

#: type can be global or pose
type: str = "global"

@staticmethod
def get_raw_request(
method: str, params: SmartRequestParams | None = None
Expand Down Expand Up @@ -429,6 +439,8 @@ def get_component_requests(component_id, ver_code):
"clean": [
SmartRequest.get_raw_request("getCleanRecords"),
SmartRequest.get_raw_request("getVacStatus"),
SmartRequest.get_raw_request("getCleanStatus"),
SmartRequest("getCleanAttr", SmartRequest.GetCleanAttrParams()),
],
"battery": [SmartRequest.get_raw_request("getBatteryInfo")],
"consumables": [SmartRequest.get_raw_request("getConsumablesInfo")],
Expand Down
6 changes: 5 additions & 1 deletion kasa/device_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def get_device_class_from_family(
"SMART.KASAHUB": SmartDevice,
"SMART.KASASWITCH": SmartDevice,
"SMART.IPCAMERA.HTTPS": SmartCamDevice,
"SMART.TAPOROBOVAC": SmartDevice,
"SMART.TAPOROBOVAC.HTTPS": SmartDevice,
"IOT.SMARTPLUGSWITCH": IotPlug,
"IOT.SMARTBULB": IotBulb,
"IOT.IPCAMERA": IotCamera,
Expand All @@ -173,6 +173,9 @@ def get_device_class_from_family(
_LOGGER.debug("Unknown SMART device with %s, using SmartDevice", device_type)
cls = SmartDevice

if cls is not None:
_LOGGER.debug("Using %s for %s", cls.__name__, device_type)

return cls


Expand All @@ -188,6 +191,7 @@ def get_protocol(config: DeviceConfig, *, strict: bool = False) -> BaseProtocol
"""
ctype = config.connection_type
protocol_name = ctype.device_family.value.split(".")[0]
_LOGGER.debug("Finding protocol for %s", ctype.device_family)

if ctype.device_family is DeviceFamily.SmartIpCamera:
if strict and ctype.encryption_type is not DeviceEncryptionType.Aes:
Expand Down
10 changes: 8 additions & 2 deletions kasa/discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,16 +676,22 @@ async def try_connect_all(
for key, val in candidates.items():
try:
prot, config = val
_LOGGER.debug("Trying to connect with %s", prot.__class__.__name__)
dev = await _connect(config, prot)
except Exception:
_LOGGER.debug("Unable to connect with %s", prot)
except Exception as ex:
_LOGGER.debug(
"Unable to connect with %s: %s",
prot.__class__.__name__,
ex,
)
if on_attempt:
ca = tuple.__new__(ConnectAttempt, key)
on_attempt(ca, False)
else:
if on_attempt:
ca = tuple.__new__(ConnectAttempt, key)
on_attempt(ca, True)
_LOGGER.debug("Found working protocol %s", prot.__class__.__name__)
return dev
finally:
await prot.close()
Expand Down
2 changes: 2 additions & 0 deletions kasa/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ def from_int(value: int) -> SmartErrorCode:
DST_ERROR = -2301
DST_SAVE_ERROR = -2302

VACUUM_BATTERY_LOW = -3001

SYSTEM_ERROR = -40101
INVALID_ARGUMENTS = -40209

Expand Down
3 changes: 3 additions & 0 deletions kasa/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ class Module(ABC):
Camera: Final[ModuleName[smartcam.Camera]] = ModuleName("Camera")
LensMask: Final[ModuleName[smartcam.LensMask]] = ModuleName("LensMask")

# Vacuum modules
Clean: Final[ModuleName[smart.Clean]] = ModuleName("Clean")

def __init__(self, device: Device, module: str) -> None:
self._device = device
self._module = module
Expand Down
2 changes: 2 additions & 0 deletions kasa/smart/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .brightness import Brightness
from .childdevice import ChildDevice
from .childprotection import ChildProtection
from .clean import Clean
from .cloud import Cloud
from .color import Color
from .colortemperature import ColorTemperature
Expand Down Expand Up @@ -66,6 +67,7 @@
"TriggerLogs",
"FrostProtection",
"Thermostat",
"Clean",
"SmartLightEffect",
"OverheatProtection",
"HomeKit",
Expand Down
Loading
Loading