feat: expose AP_SUB_MODE_LABELS for Signature/SP4i fan presets#133
Merged
Conversation
Adds a public AP_SUB_MODE_LABELS dict mapping the four `apsubmode` values observed on Blueair Blue Signature SP4i (type_name='blue40', hw='l_blue40') to their HA-side preset labels: 0 -> manual_fan, 2 -> auto, 3 -> night, 4 -> eco Signature devices declare `apsubmode` but NOT `automode` / `nightmode` in their `configuration.dc` schema, which is the capability signature `ha_blueair` uses to switch on Signature-style preset modes in the fan platform (dahlb/ha_blueair#348, #261). Also adds a fixture (tests/device_info/SP4i.json, derived from a sanitized SP4i debug log shared on ha_blueair#348) and matching SP4iTest + ModelNameTest.test_sp4i_sku_known + ApSubModeLabelsTest. No behavior change to existing devices; AP_SUB_MODE_LABELS is a new public constant only. Phase 3 of dahlb/ha_blueair#334 (tracked as ha_blueair#353) will consume this dict as the value_labels of a future FieldProfile('apsubmode').
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.
feat: expose
AP_SUB_MODE_LABELSfor Signature/SP4i fan presetsSummary
Adds a public
AP_SUB_MODE_LABELS: dict[int, str]constant toblueair_apimapping the fourapsubmodeinteger values observed onSignature-series air purifiers to their consumer-facing preset labels:
0manual_fan2auto3night4ecoThe constant is consumed by
dahlb/ha_blueair#348/#261to expose Signature fan preset modes in Home Assistant. This PR ships
only the library piece (constant + fixture + tests); the
integration change is a separate PR that will pin to the release that
ships from this PR.
No behavior change to any existing device —
apsubmodewas alreadyplumbed end-to-end through
SHADOW_FIELD_MAP,refresh(), andset_ap_sub_modein v1.51.0. This PR adds only the public labelmap and regression coverage.
Why a library constant (vs. in
ha_blueair)?Three reasons:
ha_blueair#353) — theschema-driven entity discovery work explicitly designs a per-slug
FieldProfileregistry whosevalue_labelsfield is exactly thisdict's shape. Keeping the mapping in
blueair_apilets the futureregistry import it directly instead of re-discovering the SP4i
values.
(Home Assistant integration, CLI, dashboard) wants the same
preset list, they should not each maintain their own copy.
MQTT_SENSOR_FIELD_MAPandSHADOW_FIELD_MAPalready live indevice_aws.py; this is thesame idiom.
Why a constant and not an Enum or Literal type?
The
apsubmodeshadow slug is polyvalent across device families(see the constant's docstring). An
Enumdeclaring "the legal valuesof
apsubmode" would either:declares the slug but ignores it), or
current state of the codebase asks for.
A plain dict keyed by int with a long disambiguating docstring keeps
the contract honest: this map is Signature-family only, NOT a
global
apsubmodedecoder. The docstring spells out the three knownnamespaces and how
ha_blueair's fan platform keeps them disjointvia a capability gate (
ap_sub_mode != NotImplemented AND fan_auto_mode == NotImplemented AND night_mode == NotImplemented).What's in this PR
src/blueair_api/device_aws.pyAP_SUB_MODE_LABELSwith a docstring thatexplains polyvalent-slug pitfalls and Phase 3 forward compatibility.
src/blueair_api/__init__.pyAP_SUB_MODE_LABELSfrom the package root sodownstream consumers can
from blueair_api import AP_SUB_MODE_LABELS(stable import contract).tests/device_info/SP4i.jsondevice_inforesponseshared by @Pazuzu6666 in
ha_blueair#348.All user-identifying tokens (UUIDs, MAC, serial, account ID,
Wi-Fi credentials, home address, lat/lon, auth tokens) are
replaced with synthetic placeholders following the same
convention as the existing
T10i.jsonandpet_air_pro.jsonfixtures.
tests/test_device_aws.pySP4iTestmirrorsT10iTest/Max211iTest. Loads thefixture, runs
refresh(), and asserts the full attribute setvia
assert_fully_checked. Pinpoints the capability signature:ap_sub_mode == 2fan_auto_mode is NotImplementednight_mode is NotImplementedmodel_name == "Blueair Blue Signature SP4i"(SKU112936)mqtt_sensor_slugs == ["pm1", "pm2_5", "pm10", "rssi"]ModelNameTest.test_sp4i_sku_knowncovers the SKU tableentry directly.
ApSubModeLabelsTest(4 tests):test_labels_match_known_signature_mapping— locks thecanonical 0/2/3/4 → manual_fan/auto/night/eco mapping.
test_labels_are_unique— guards against silent reverse-lookupbreakage.
test_labels_are_well_formed— int keys, non-empty stringvalues.
test_publicly_importable_from_package_root— protects the__init__.pyre-export against future refactors.Validation
Versioning
Suggest v1.51.1 patch release after merge — additive public API,
no behavior change, no migration concerns.
Related
dahlb/ha_blueair#348—integration-side fan-platform change ships in a separate PR
against
dahlb/ha_blueair:mainafter this lands.dahlb/ha_blueair#261(Signature Modes umbrella).dahlb/ha_blueair#353(Phase 3schema-driven entity discovery) — the registry's
FieldProfile("apsubmode")will consume this dict asvalue_labelsonce that work starts.