Files
localthings/tests/test_air_purifier_fan_discriminator.py
T
Marc Billow 4e1eb7d03d Add fan-mode control for TP1X_DA-AC-AIR air purifiers (issue #130)
This newer board family reports fan modes (Smart/Max/Mid/WindFree/Sleep)
directly on /mode/vs/0's top-level modes/supportedModes fields, unlike
the older ARTIK051_TVTL family this registry already supported, which
packs everything into an options[] array with no usable fan-speed
selector at all (see the module docstring's Comode_Off finding). Both
board generations share the /mode/vs/0 href, so the existing MODE
capability and a new FAN capability are discriminated by a match_fn
checking for the top-level supportedModes field, rather than adding a
new device type.

The fan entity only exposes PRESET_MODE, not an ordered percentage --
WindFree/Smart/Sleep are named behaviors, not "faster/slower" positions
relative to Max/Mid, matching how the AC family's own named convenient
modes are modeled as a preset rather than a speed number.

Also picked up the rest of this board's previously-unbound hrefs while
in there (display, HEPA filter, panel status, pet-filter mode, sound
settings), reusing airconditioner.DISPLAY_LIGHT and
airconditioner.MUTE_ONCE for the two hrefs identical to the shared
DA-AC- board family, since the "incomplete capability coverage" repair
was firing on more than just the fan gap the issue described.
2026-07-27 14:08:32 +00:00

40 lines
1.5 KiB
Python

"""Tests for the /mode/vs/0 match_fn discriminator in capabilities/
air_purifier.py (issue #130).
`_has_top_level_modes` decides whether a given air-purifier dump's shared
/mode/vs/0 href binds to the older ARTIK051_TVTL family's MODE capability
(packed options[] scheme) or the newer TP1X_DA-AC-AIR family's FAN
capability (modes/supportedModes reported directly). Pure function, no
coordinator/entity dependency needed to test it directly -- same rationale
as test_climate_wind_oscillation_fallback.py's `_oscillation_swing`.
"""
from custom_components.localthings.registry.capabilities.air_purifier import (
_has_top_level_modes,
)
def test_new_family_with_supported_modes_list_matches():
rep = {
'x.com.samsung.da.modes': ['Smart'],
'x.com.samsung.da.supportedModes': ['Smart', 'Max', 'Mid', 'WindFree', 'Sleep'],
}
assert _has_top_level_modes(rep, {}) is True
def test_old_family_with_no_supported_modes_field_does_not_match():
"""The ARTIK051_TVTL family's /mode/vs/0 packs everything into
options[] (Light_On, Comode_Off, ...) and has no top-level
supportedModes at all."""
rep = {
'x.com.samsung.da.options': ['Light_On', 'Comode_Off', 'OptionCode_60282'],
}
assert _has_top_level_modes(rep, {}) is False
def test_non_list_supported_modes_does_not_match():
assert _has_top_level_modes({'x.com.samsung.da.supportedModes': 'Smart'}, {}) is False
def test_empty_rep_does_not_match():
assert _has_top_level_modes({}, {}) is False