Repository navigation
fix: send state-changing commands once, stop bypassing the rate limiter - #51
Merged
Merged
Conversation
Read-only audit of 0.0.38: state-changing GET commands are retried (double actuation), the rate-limiter timeout bypasses the limiter, readings parsers crash on list values, the aiohttp upper bound will block installation inside Home Assistant, plus smaller defects, test gaps and documentation drift. Each item names the fix and the test to write first. Shared package IDs with violet-hass/docs/IMPROVEMENT_PLAN.md. No code is changed by this commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jZ4x6m2TDjVGmvX5Up5Ge
A full read-only audit of 0.0.38. The two safety fixes are the reason this lands as one change: both could act on the pool without anyone asking. Retryability was decided by HTTP method, but the controller applies almost every state change through GET. A setFunctionManually request that timed out after the controller applied it was repeated up to max_retries times; for the PUSH actions - the digital-rule trigger and the cover commands - that undid the change or moved the cover twice. Command endpoints are now listed in NON_RETRYABLE_ENDPOINTS and sent exactly once whatever their method. When the wait for a rate-limit token expired, the request was sent anyway without a token, so every caller that waited out the timeout hit the controller at once - the pile-up the limiter exists to prevent. It now fails. Also fixed: typed readings accessors raised TypeError on the list values the firmware documents; an HTML login page opened the circuit breaker and hid the payload error; set_config rewrote invalid keys into different real settings and coerced None and lists into 'None' and '1 2'; sanitize_numeric turned '1,5' into 15.0; credentials in a host string reached the exception message; disabling certificate verification performed a blocking CA-store read inside the event loop. The aiohttp <3.15 cap is removed: Home Assistant follows aiohttp closely and the cap would have made pip refuse this package inside HA. Eight regression tests added, 250 -> 260. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jZ4x6m2TDjVGmvX5Up5Ge
Xerolux
marked this pull request as ready for review
September 8, 2026 18:57
Merged
13 of 17 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A full read-only audit of 0.0.38, and the fixes for what it found. The audit's work order is included as
docs/IMPROVEMENT_PLAN.md.The two reasons this release exists. Both could act on the pool without anyone asking.
GET. AsetFunctionManuallyrequest that timed out after the controller applied it was repeated up tomax_retriestimes. For thePUSHactions, the digital-rule trigger and the cover commands, that undid the change or moved the cover a second time;initUpdateandsetOutputTestmodeare not idempotent either. Command endpoints are now listed inNON_RETRYABLE_ENDPOINTSand sent exactly once whatever their method, and each command method also passesretryable=Falseexplicitly. Reads keep their retries.Also fixed
TypeErroron the list values the firmware documents (DOS_*_STATEis "LIST, STRING"), crashing the consumer instead of returningNone.set_config()stripped invalid characters from keys, soDOSAGE_ph.minussilently became the real, different settingDOSAGE_phminus; it also coercedNoneand lists into"None"and"1 2", and non-finite floats into0.0. All of that raises now.validate_device_key()additionally stopped upper-casing, because controller keys are case-sensitive (pH_value).sanitize_numeric("1,5")returned15.0. A single comma is read as a decimal separator before the digit-extraction fallback runs;"12.5 mL"still yields its number.ssl=Falseinstead.get_log()returned[]forhas_moreand left\ron CRLF lines;manual_dosing()raised a bareTypeErrorfor a non-numeric duration;validate_duration(True)counted as one second;parse_uptime_string()returned zero if seconds appeared;parse_epoch_seconds()turned negative values into 1969 dates;_command_result()returned a dict body without thesuccesskey.Behaviour and API surface
aiohttp<3.15cap is removed. Home Assistant follows aiohttp closely and the cap would have madepiprefuse this package inside Home Assistant the day core moved past it.const_api.pydocuments: state changesCRITICAL,set_config()HIGH, history and logsLOW. A pump-off command no longer queues behind sensor polling.validate_duration,RS485_PUMP_NAMES,DEVICE_STATE_MAPPING,SWITCH_FUNCTIONS,API_PRIORITY_*,CircuitBreakerState, and more).SWITCH_FUNCTIONSlabels are English; consumers that displayed them see changed strings._trigger_dosing()gained asourceargument for the firmware'sfromfield. H2O2 manual dosing stays unsupported until the mapping is confirmed against firmware.Verification:
ruffclean,mypyclean,pytest -q tests250 to 260 (eight new regression tests: switch commands, rule triggers andinitUpdatesent once while reads still retry; a limiter timeout sends nothing; an HTML page does not open the breaker; the hostname error hides credentials; underscores accepted). The smoke test against the mock server passes. Three tests that codified the old wrong behaviour were rewritten to assert the new one, and each says why in its docstring.🤖 Generated with Claude Code
https://claude.ai/code/session_018jZ4x6m2TDjVGmvX5Up5Ge