fix: separate LUH-O451S-WUS humidity range and mist modes from WUSR/601S - #547
Open
proscar87 wants to merge 5 commits into
Open
fix: separate LUH-O451S-WUS humidity range and mist modes from WUSR/601S#547proscar87 wants to merge 5 commits into
proscar87 wants to merge 5 commits into
Conversation
…U) (webdjoe#502) ## Summary Adds RGB nightlight control support for the `VeSyncHumid200300S` device class, specifically for the **LUH-O451S-WEU** (OasisMist 4.5L) humidifier model. This addresses missing nightlight functionality that prevents Home Assistant from creating light entities for humidifiers with RGB nightlight features. Related issue: [home-assistant/core#160387](home-assistant/core#160387) ## Changes - Added `HumidifierFeatures.RGB_NIGHTLIGHT` feature flag - Added `supports_rgb_nightlight` property to base class - Implemented `set_rgb_nightlight(power, brightness, red, green, blue)` method - Added RGB nightlight state attributes (status, brightness, r/g/b, color_mode) ## API Quirks 1. **Brightness-adjusted RGB**: API expects RGB values pre-multiplied by brightness via HSV conversion 2. **Color slider location**: API requires a `colorSliderLocation` (0-100) mapped from an 8-color gradient 3. **!Stale API responses!**: After setting values, API returns old data for several minutes - implemented timeout to prevent state drift. This is sadly not working perfectly yet, I didn't find out how to improve this behavior or force the getHumidifierStatus to return the values we just set before. Maybe someone with more insight or time can improve this as it's also causing issues when updating the nightlight via App and then using this library. ## Known Limitations - Minimum brightness is 40% (enforced by VeSync app) - Other models with RGB nightlights (e.g., LUH-D301S-WUSR) may work by adding the feature flag - only tested on LUH-O451S-WEU ## Testing Tested on physical LUH-O451S-WEU device: power on/off, brightness, color changes, state refresh.
Add WYLDR16A1081 to the device map as a dedicated entry with a new VeSyncWYLDRPlug class. This device is functionally identical to the BSDOG01 series (on/off, real-time power, voltage, daily energy) but does not support energy history retrieval via the bypassV2 API — calls to getEnergyHistory return result code -1 for this device type. VeSyncWYLDRPlug overrides _get_energy_history() with a no-op so that weekly and monthly history requests are silently skipped instead of generating spurious warning logs on every poll cycle.
…bdjoe#514) Using loop.stop() leaves the loop open, which can cause 'Too many open files' errors when running the test suite.
…B381S) (webdjoe#528) ## Summary The LEH-B381S (Sprout Humidifier) already declares `NIGHTLIGHT` / `NIGHTLIGHT_BRIGHTNESS` support in `device_map.py`, and `VeSyncSproutHumid` already had a working private `_set_nightlight_state` helper that builds the correct `setLightStatus` payload. However, the class never overrode the base class's `set_nightlight_brightness` / `toggle_nightlight` methods, so any caller (e.g. Home Assistant's `select`/`switch` entities) fell through to the base-class stub, which just logs `Nightlight brightness has not been configured.` and returns `False` instead of calling the API. Traceback this was pulled from (Home Assistant, via `select.async_select_option`): ``` File ".../vesync/select.py", line 194, in async_select_option ERROR [pyvesync.base_devices.humidifier_base] Nightlight brightness has not been configured. ``` - Added `set_nightlight_brightness` and `toggle_nightlight` overrides to `VeSyncSproutHumid`, wired to the existing `_set_nightlight_state` helper (mirroring the pattern used in `VeSyncHumid200300S`/`VeSyncHumid1000S`). - Fixed a latent bug in `_set_nightlight_state`: it unconditionally stored the passed-in `brightness` (including `None`) as the new state, and would send `"brightness": null` to the API when toggling the light before a brightness had ever been read or set. It now falls back to the last known brightness, or `100` if none is known yet. ## Test plan - [x] Added `turn_on_nightlight`, `turn_off_nightlight`, and `set_nightlight_brightness` test cases for `LEH-B381S` in `test_humidifiers.py` - [x] Recorded new API fixtures in `src/tests/api/vesynchumidifier/LEH-B381S.yaml` - [x] `pytest src/tests/` — 355 passed, 3 skipped - [x] `mypy src/pyvesync/devices/vesynchumidifier.py` — no issues - [ ] Validated against a physical LEH-B381S device (reporter has the device; will confirm in this thread) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Joe Trabulsy <jtrabulsy@gmail.com>
configModule WFON_AHM_LUH-A451S-WUS_US only accepts a target humidity of
40-80 (the cloud API rejects 30 with "target humidity is out of range")
and rejects the HUMIDITY mist mode ("Mode value invaild!"), unlike the
WUSR/601S variants it was grouped with, which the maintainer confirmed
in the issue threads can go down to 30.
Splits the single HumidifierMap entry for
['LUH-O451S-WUS', 'LUH-O451S-WUSR', 'LUH-O601S-WUS', 'LUH-O601S-KUS']
into two: LUH-O451S-WUS keeps target_minmax=(40, 80) and drops the
HUMIDITY mode, the other three dev_types keep the previous (30, 80)
range and HUMIDITY mode, matching the only variant with confirmed
API-rejected behavior.
Fixes webdjoe#295, fixes webdjoe#296.
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.
Problem
The
HumidifierMapentry fordev_types=['LUH-O451S-WUS', 'LUH-O451S-WUSR', 'LUH-O601S-WUS', 'LUH-O601S-KUS']treats all four dev types identically:target_minmax=(30, 80)(the class default) and aHUMIDITYmist mode.Both #295 and #296 show this is wrong for the
LUH-O451S-WUSvariant specifically (configModuleWFON_AHM_LUH-A451S-WUS_US):"target humidity is out of range"— this device only accepts 40-80.humidityis rejected with"Mode value invaild!".@webdjoe confirmed in #296 that this is specific to that configModule, and that the WUSR variant (
WFON_AHM_LUH-A451S-WUSR_US) can go down to 30.Fix
Split the single
HumidifierMapentry into two:dev_types=['LUH-O451S-WUS']:target_minmax=(40, 80),HUMIDITYmode removed.dev_types=['LUH-O451S-WUSR', 'LUH-O601S-WUS', 'LUH-O601S-KUS']: unchanged (target_minmax=(30, 80),HUMIDITYmode kept) — no evidence these are affected.target_minmaxandmist_modesaren't just documentation —set_humidity()/set_humidity_mode()validate against them client-side before calling the API (vesynchumidifier.py), so this actually prevents the rejected calls rather than just describing them.Splitting the dev_types list also means
LUH-O451S-WUSRnow gets its own entry inhumidifier_modules, which the test suite auto-parametrizes from (HUMIDIFIERS = [m.setup_entry for m in humidifier_modules]) — added the corresponding fixtures (HUMIDIFIER_DETAILS/DETAILS_RESPONSES/METHOD_RESPONSESentries, recorded YAML) so it's exercised the same as every other device.Testing
Added 4 regression tests in
test_humidifiers.py:test_oasismist_450s_wus_rejects_humidity_below_40/test_oasismist_450s_wusr_still_allows_humidity_30test_oasismist_450s_wus_has_no_humidity_mode/test_oasismist_450s_wusr_still_has_humidity_modeConfirmed all 4 fail against the pre-fix code (
git stashon justdevice_map.py/call_json_humidifiers.py) with the exact errors from the two issues, and pass with the fix. Full suite: 395 passed, 3 skipped.flake8 src/pyvesync,pylint src/pyvesync(10.00/10),ruff check src/pyvesync/device_map.py(only pre-existing, unrelatedCPY001finding, confirmed present onmastertoo) all clean.mypy src/pyvesynchas 2 pre-existing errors invesyncthermostat.py, unrelated to this change and present onmaster.Not validated against real hardware (no OasisMist 450S on hand) — the fix is based directly on the live API traces both issues include and @webdjoe's own confirmation of the configModule-specific behavior, not guesswork about the device.
Fixes #295, fixes #296.