fix: normalize washer dosing-select codes; stop blocking the loop in diagnostics (#9)

Two fixes for issue #9 (WW90T634DHE washer):

- washer dosing selects: the four detergent/softener dosing selects read their
  current value from `<Prefix>LevelCtrl_<code>` (un-padded, e.g. "3") but their
  options from `Supported<Prefix>LevelCtrl_<hexpairs>` (zero-padded, e.g. "03").
  HA's SelectEntity renders a select "unknown" whenever current_option is not in
  options, so all four sat "unknown" (idle and running) even though every other
  select worked -- which is why it was only those four. Normalize the current
  value to the supported code with the same integer value so it matches an
  option (and its translation); convert back to the device's native un-padded
  format on write.

- diagnostics: pkg_version("smartthings-local") reads package metadata off disk
  (listdir + open + read_text), tripping HA's event-loop blocking-call detector.
  Offload it to the executor. Audited the rest of the package: config_flow's
  socket/crypto and every coordinator DTLS call are already offloaded via
  async_add_executor_job -- this was the only blocking call left on the loop.

Also documents air-conditioner support in the README (device table, capability
module list, platform list), missed when that support landed.

Updates the washer dosing tests to the corrected value/write format and adds a
current-option-is-a-valid-option regression; adds a diagnostics test asserting
the version lookup runs off the event loop.
This commit is contained in:
Marc Billow
2026-07-21 23:49:30 +00:00
parent bedc662b47
commit c0298d57d9
5 changed files with 109 additions and 19 deletions
+4 -3
View File
@@ -16,6 +16,7 @@ Your state stays on your LAN: HA talks to the appliance over a direct DTLS sessi
| Type | Registry |
|---|---|
| Air conditioner | `by_type/airconditioner.py` |
| Dryer | `by_type/dryer.py` |
| Oven | `by_type/oven.py` |
| Dishwasher | `by_type/dishwasher.py` |
@@ -99,7 +100,7 @@ custom_components/localthings/
diagnostics.py Redacted diagnostics download (device state + coverage metadata)
const.py Domain, config keys, probe ports
entity.py Base entity wiring capability registry -> HA entity
sensor.py / binary_sensor.py / switch.py / number.py / select.py / button.py / time.py
sensor.py / binary_sensor.py / switch.py / number.py / select.py / button.py / time.py / climate.py
One module per HA platform
strings.json / translations/ Config-flow copy + entity state translations
registry/
@@ -110,8 +111,8 @@ custom_components/localthings/
adapter.py Flattens bound entities into HA-ready state
identity.py Reads device identity for type detection
redact.py Strips account/identity data before diagnostics leave HA
capabilities/ Shared + per-family Capability definitions (common, dryer, oven,
dishwasher, fridge, washer, laundry, operational, ignored)
capabilities/ Shared + per-family Capability definitions (common, airconditioner,
dryer, oven, dishwasher, fridge, washer, laundry, operational, ignored)
by_type/ One DeviceRegistry per appliance type, composed from capabilities/
tests/ Registry composition, discovery, entity descriptors, coordinator/observe
behavior, and golden-file regression against captured device dumps