Skip to content

Conversation

@Lash-L
Copy link
Collaborator

@Lash-L Lash-L commented Dec 7, 2025

A01 values were not converted after the refactor.

This combined with the changes in #645 have A01 working tested with a shared account

Fixes: #623

@codecov
Copy link

codecov bot commented Dec 7, 2025

Codecov Report

❌ Patch coverage is 92.85714% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
roborock/devices/traits/a01/__init__.py 84.00% 2 Missing and 2 partials ⚠️
Files with missing lines Coverage Δ
tests/devices/test_a01_traits.py 100.00% <100.00%> (ø)
tests/devices/traits/a01/test_init.py 100.00% <100.00%> (ø)
roborock/devices/traits/a01/__init__.py 73.21% <84.00%> (+5.56%) ⬆️

... and 14 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where A01 device values (Dyad and Zeo products) were not being converted after a previous refactor. The fix adds value conversion logic to transform raw protocol responses into properly typed and formatted values.

Key changes:

  • Implemented protocol-to-value conversion mappings for Dyad and Zeo devices
  • Updated query_values methods to apply conversions to raw responses
  • Added comprehensive test coverage for the conversion logic

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 29 comments.

File Description
roborock/devices/traits/a01/__init__.py Added conversion dictionaries (DYAD_PROTOCOL_ENTRIES, ZEO_PROTOCOL_ENTRIES) and converter functions (convert_dyad_value, convert_zeo_value) to transform raw protocol values; updated DyadApi.query_values and ZeoApi.query_values to apply conversions to responses
tests/devices/test_a01_traits.py Added new test file with tests for DyadApi.query_values and ZeoApi.query_values to verify that raw protocol values are correctly converted to their appropriate types (enums to strings, integers, booleans, etc.)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 9 to 15
@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
# Implementation detail: we need to mock send_decoded_command where it is used.
return channel
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock_channel fixture is defined but never used. It should be removed or utilized in the tests.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm a bit confused by this one as mock_channel is used

Comment on lines 19 to 22
@pytest.fixture
def mock_send_decoded_command():
with patch("roborock.devices.traits.a01.send_decoded_command", new_callable=AsyncMock) as mock:
yield mock
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock_send_decoded_command fixture is defined but never used in the tests (both tests create their own patches inline). This fixture should be removed or the tests should be refactored to use it.

Suggested change
@pytest.fixture
def mock_send_decoded_command():
with patch("roborock.devices.traits.a01.send_decoded_command", new_callable=AsyncMock) as mock:
yield mock

Copilot uses AI. Check for mistakes.
Comment on lines 58 to 55
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
# From a01_conversions.py: RoborockZeoProtocol.STATE: lambda val: ZeoState(val).name
assert result[RoborockZeoProtocol.STATE] == "spinning" # Assuming ZeoState(6).name is spinning
assert result[RoborockZeoProtocol.COUNTDOWN] == 120
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test does not verify that send_decoded_command was called with the correct parameters. Consider adding an assertion like mock_send.assert_called_once_with(mock_channel, {RoborockZeoProtocol.ID_QUERY: [int(p) for p in protocols]}) to ensure the query is constructed correctly.

Copilot uses AI. Check for mistakes.
}

ZEO_PROTOCOL_ENTRIES: dict[RoborockZeoProtocol, Callable] = {
# ro
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment # ro appears incomplete or unclear. It should be removed or expanded to explain what it means (e.g., "read-only" if that's the intent).

Suggested change
# ro
# read-only

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +81
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),
Copy link

Copilot AI Dec 7, 2025

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.

Suggested change
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 uses AI. Check for mistakes.
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),
Copy link

Copilot AI Dec 7, 2025

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 uses AI. Check for mistakes.
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),
Copy link

Copilot AI Dec 7, 2025

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 uses AI. Check for mistakes.
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),
Copy link

Copilot AI Dec 7, 2025

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.

Suggested change
RoborockZeoProtocol.SOFTENER_EMPTY: lambda val: bool(val),
RoborockZeoProtocol.SOFTENER_EMPTY: bool,

Copilot uses AI. Check for mistakes.
@allenporter
Copy link
Contributor

Issue #623

if (converter := DYAD_PROTOCOL_ENTRIES.get(protocol_value)) is not None:
try:
return converter(value)
except (ValueError, TypeError):
Copy link
Contributor

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

params = {RoborockDyadDataProtocol.ID_QUERY: str([int(p) for p in protocols])}
return await send_decoded_command(self._channel, params)
response = await send_decoded_command(self._channel, params)
return {protocol: convert_dyad_value(protocol, response.get(protocol)) for protocol in protocols}
Copy link
Contributor

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.

@allenporter
Copy link
Contributor

Feel free to merge once you've reviewed my changes if you think they're ready.

@Lash-L Lash-L merged commit f875e7a into main Dec 7, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add conversions for DYAD_PROTOCOL_ENTRIES

3 participants