-
Notifications
You must be signed in to change notification settings - Fork 57
fix: convert a01 values #647
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,31 @@ | ||||||||||||||||||||||||||||||||||||||||||
| from collections.abc import Callable | ||||||||||||||||||||||||||||||||||||||||||
| from datetime import time | ||||||||||||||||||||||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from roborock.data import HomeDataProduct, RoborockCategory | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.data import DyadProductInfo, DyadSndState, HomeDataProduct, RoborockCategory | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.data.dyad.dyad_code_mappings import ( | ||||||||||||||||||||||||||||||||||||||||||
| DyadBrushSpeed, | ||||||||||||||||||||||||||||||||||||||||||
| DyadCleanMode, | ||||||||||||||||||||||||||||||||||||||||||
| DyadError, | ||||||||||||||||||||||||||||||||||||||||||
| DyadSelfCleanLevel, | ||||||||||||||||||||||||||||||||||||||||||
| DyadSelfCleanMode, | ||||||||||||||||||||||||||||||||||||||||||
| DyadSuction, | ||||||||||||||||||||||||||||||||||||||||||
| DyadWarmLevel, | ||||||||||||||||||||||||||||||||||||||||||
| DyadWaterLevel, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadStateCode, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.data.zeo.zeo_code_mappings import ( | ||||||||||||||||||||||||||||||||||||||||||
| ZeoDetergentType, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoDryingMode, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoError, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoMode, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoProgram, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoRinse, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoSoftenerType, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoSpin, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoState, | ||||||||||||||||||||||||||||||||||||||||||
| ZeoTemperature, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.devices.a01_channel import send_decoded_command | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.devices.mqtt_channel import MqttChannel | ||||||||||||||||||||||||||||||||||||||||||
| from roborock.devices.traits import Trait | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -12,6 +37,81 @@ | |||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| DYAD_PROTOCOL_ENTRIES: dict[RoborockDyadDataProtocol, Callable] = { | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.STATUS: lambda val: RoborockDyadStateCode(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.SELF_CLEAN_MODE: lambda val: DyadSelfCleanMode(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.SELF_CLEAN_LEVEL: lambda val: DyadSelfCleanLevel(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.WARM_LEVEL: lambda val: DyadWarmLevel(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.CLEAN_MODE: lambda val: DyadCleanMode(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.SUCTION: lambda val: DyadSuction(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.WATER_LEVEL: lambda val: DyadWaterLevel(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.BRUSH_SPEED: lambda val: DyadBrushSpeed(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.POWER: lambda val: int(val), | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.AUTO_DRY: lambda val: bool(val), | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.MESH_LEFT: lambda val: int(360000 - val * 60), | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.BRUSH_LEFT: lambda val: int(360000 - val * 60), | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.ERROR: lambda val: DyadError(val).name, | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.VOLUME_SET: lambda val: int(val), | ||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.STAND_LOCK_AUTO_RUN: lambda val: bool(val), | ||||||||||||||||||||||||||||||||||||||||||
allenporter marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.AUTO_DRY_MODE: lambda val: bool(val), | ||||||||||||||||||||||||||||||||||||||||||
allenporter marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.SILENT_DRY_DURATION: lambda val: int(val), # in minutes | ||||||||||||||||||||||||||||||||||||||||||
allenporter marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.SILENT_MODE: lambda val: bool(val), | ||||||||||||||||||||||||||||||||||||||||||
allenporter marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+49
to
+58
|
||||||||||||||||||||||||||||||||||||||||||
| RoborockDyadDataProtocol.POWER: lambda val: int(val), | |
| RoborockDyadDataProtocol.AUTO_DRY: lambda val: bool(val), | |
| RoborockDyadDataProtocol.MESH_LEFT: lambda val: int(360000 - val * 60), | |
| RoborockDyadDataProtocol.BRUSH_LEFT: lambda val: int(360000 - val * 60), | |
| RoborockDyadDataProtocol.ERROR: lambda val: DyadError(val).name, | |
| RoborockDyadDataProtocol.VOLUME_SET: lambda val: int(val), | |
| RoborockDyadDataProtocol.STAND_LOCK_AUTO_RUN: lambda val: bool(val), | |
| RoborockDyadDataProtocol.AUTO_DRY_MODE: lambda val: bool(val), | |
| RoborockDyadDataProtocol.SILENT_DRY_DURATION: lambda val: int(val), # in minutes | |
| RoborockDyadDataProtocol.SILENT_MODE: lambda val: bool(val), | |
| RoborockDyadDataProtocol.POWER: int, | |
| RoborockDyadDataProtocol.AUTO_DRY: bool, | |
| RoborockDyadDataProtocol.MESH_LEFT: lambda val: int(360000 - val * 60), | |
| RoborockDyadDataProtocol.BRUSH_LEFT: lambda val: int(360000 - val * 60), | |
| RoborockDyadDataProtocol.ERROR: lambda val: DyadError(val).name, | |
| RoborockDyadDataProtocol.VOLUME_SET: int, | |
| RoborockDyadDataProtocol.STAND_LOCK_AUTO_RUN: bool, | |
| RoborockDyadDataProtocol.AUTO_DRY_MODE: bool, | |
| RoborockDyadDataProtocol.SILENT_DRY_DURATION: int, # in minutes | |
| RoborockDyadDataProtocol.SILENT_MODE: bool, |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
| RoborockZeoProtocol.COUNTDOWN: lambda val: int(val), | |
| RoborockZeoProtocol.WASHING_LEFT: lambda val: int(val), | |
| RoborockZeoProtocol.ERROR: lambda val: ZeoError(val).name, | |
| RoborockZeoProtocol.TIMES_AFTER_CLEAN: lambda val: int(val), | |
| RoborockZeoProtocol.DETERGENT_EMPTY: lambda val: bool(val), | |
| RoborockZeoProtocol.SOFTENER_EMPTY: lambda val: bool(val), | |
| RoborockZeoProtocol.COUNTDOWN: int, | |
| RoborockZeoProtocol.WASHING_LEFT: int, | |
| RoborockZeoProtocol.ERROR: lambda val: ZeoError(val).name, | |
| RoborockZeoProtocol.TIMES_AFTER_CLEAN: int, | |
| RoborockZeoProtocol.DETERGENT_EMPTY: bool, | |
| RoborockZeoProtocol.SOFTENER_EMPTY: bool, |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
| RoborockZeoProtocol.SOFTENER_EMPTY: lambda val: bool(val), | |
| RoborockZeoProtocol.SOFTENER_EMPTY: bool, |
allenporter marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added exception catching here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this to use the requested protocols to decide which values to return.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| from unittest.mock import AsyncMock, Mock, patch | ||
|
|
||
| import pytest | ||
|
|
||
| from roborock.devices.traits.a01 import DyadApi, ZeoApi | ||
| from roborock.roborock_message import RoborockDyadDataProtocol, RoborockZeoProtocol | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_channel(): | ||
| channel = Mock() | ||
| channel.send_command = AsyncMock() | ||
| # Mocking send_decoded_command if it was a method on channel, but it's a standalone function imported in traits. | ||
| # However, in traits/__init__.py it is imported as: from roborock.devices.a01_channel import send_decoded_command | ||
| return channel | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_dyad_query_values(mock_channel): | ||
| with patch("roborock.devices.traits.a01.send_decoded_command", new_callable=AsyncMock) as mock_send: | ||
| api = DyadApi(mock_channel) | ||
|
|
||
| # Setup mock return value (raw values) | ||
| mock_send.return_value = { | ||
| int(RoborockDyadDataProtocol.CLEAN_MODE): 1, | ||
| int(RoborockDyadDataProtocol.POWER): 100, | ||
| } | ||
|
|
||
| protocols = [RoborockDyadDataProtocol.CLEAN_MODE, RoborockDyadDataProtocol.POWER] | ||
| result = await api.query_values(protocols) | ||
|
|
||
| # Verify conversion | ||
| assert RoborockDyadDataProtocol.CLEAN_MODE in result | ||
| assert RoborockDyadDataProtocol.POWER in result | ||
|
|
||
| assert isinstance(result[RoborockDyadDataProtocol.CLEAN_MODE], str) | ||
| assert result[RoborockDyadDataProtocol.POWER] == 100 | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_zeo_query_values(mock_channel): | ||
| with patch("roborock.devices.traits.a01.send_decoded_command", new_callable=AsyncMock) as mock_send: | ||
| api = ZeoApi(mock_channel) | ||
|
|
||
| mock_send.return_value = { | ||
| int(RoborockZeoProtocol.STATE): 6, # spinning | ||
| int(RoborockZeoProtocol.COUNTDOWN): 120, | ||
| } | ||
|
|
||
| protocols = [RoborockZeoProtocol.STATE, RoborockZeoProtocol.COUNTDOWN] | ||
| result = await api.query_values(protocols) | ||
|
|
||
| assert RoborockZeoProtocol.STATE in result | ||
| assert result[RoborockZeoProtocol.STATE] == "spinning" | ||
| assert result[RoborockZeoProtocol.COUNTDOWN] == 120 |
Uh oh!
There was an error while loading. Please reload this page.