Harden API client resilience and packaging#44
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the monolithic VioletPoolAPI client by splitting it into domain-specific mixins (_api_dosing.py, _api_outputs.py, _api_readings.py, and _api_system.py), modernizes the packaging configuration by migrating from setup.py to pyproject.toml, and enhances the RateLimiter with priority-based queuing. Additionally, it updates the CircuitBreaker to prevent concurrent recovery probes, switches basic authentication to use standard Authorization headers, and adds input sanitization for non-finite float values. The review feedback suggests robustly handling different line endings in _api_system.py by using splitlines() instead of split on newline, and validating/clamping the speed parameter in control_pump within _api_outputs.py to prevent sending invalid values.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| priority=API_PRIORITY_NORMAL, | ||
| ) | ||
| text = resp.strip() if resp else "" | ||
| lines = text.split("\n") if text else [] |
There was a problem hiding this comment.
Using split("\n") can leave trailing \r characters if the controller returns \r\n line endings. Using splitlines() is more robust as it automatically handles different line endings and strips the line break characters.
| lines = text.split("\n") if text else [] | |
| lines = text.splitlines() if text else [] |
| return await self.set_switch_state( | ||
| key="PUMP", | ||
| action=action, | ||
| duration=duration, | ||
| last_value=speed, | ||
| ) |
There was a problem hiding this comment.
In control_pump, the speed parameter is passed directly to set_switch_state without any validation or clamping. If action is "ON" and speed is None or an invalid value (like 5), it could send an invalid speed to the controller. Clamping/validating speed if provided ensures consistency with set_pump_speed.
| return await self.set_switch_state( | |
| key="PUMP", | |
| action=action, | |
| duration=duration, | |
| last_value=speed, | |
| ) | |
| safe_speed = max(1, min(3, int(speed))) if speed is not None else None | |
| return await self.set_switch_state( | |
| key="PUMP", | |
| action=action, | |
| duration=duration, | |
| last_value=safe_speed, | |
| ) |
Summary
Verification
python -m ruff check ..tox/py312/bin/python -m mypy violet_poolcontroller_api.tox/py312/bin/python -m pytest -q -s -W error::DeprecationWarning(203 passed).tox/py312/bin/python tests/test_api_smoke.py --user admin --password secret(66 passed, 0 failed, 0 skipped).tox/py312/bin/python -m build --no-isolation.tox/py312/bin/python -m twine check dist/*