Commit Graph

4 Commits (c8345e12bcb67d18f90815e5d01c847ae3849136)

Author SHA1 Message Date
SchiZzA 624de521c4
feat(wslink): implement every sensor the WSLink API v0.6 defines
Coverage went from 54 of 107 upload parameters to all 107, adding roughly 34
entities. New families: Type5 lightning, Type6 water leak (CH1-7), Type8
particulate matter, Type10 CO2 and Type11 CO, plus the parameters missing from
existing families - absolute pressure, feels-like temperature, the 10-minute
average wind speed, and the per-channel probe type.

Device classes follow Home Assistant's own list (PM25, PM10, AQI, CO2, CO,
DISTANCE, ATMOSPHERIC_PRESSURE, WIND_SPEED, and binary MOISTURE for leaks), so
the readings work with statistics and unit conversion without template helpers.

Batteries were the trap. The API annotates two incompatible conventions -
"(Normal=1, Low battery=0)" and "(0~5) remark: 5 is full" - and mixing them up is
silent, because a level battery read as a flag reports "not low" for every level
above empty. Eight new binary batteries (t5lsbat, t6c1-7bat) and three new level
batteries (t8bat, t10bat, t11bat) are classified from the document, and
tests/test_wslink_api_coverage.py pins each one to the wording it came from.

Water leak and battery use opposite conventions in the same family (leak 1 means
wet, battery 0 means low), so `on_value` moved into the description and one
entity class now serves both instead of two that differ by a comparison.

`t1cn` was in the API but wired to nothing, so a disconnected outdoor probe kept
publishing its last readings. Gating it required fixing the gate first: an absent
connection flag was treated as "disconnected", which would have blanked
temperature, wind and rain outright on any firmware that omits `t1cn`. Absence is
now treated as no information, and only an explicit non-1 drops the readings.

The probe type (`t234cXtp`) decides whether a channel's humidity reading is air
humidity or soil moisture, which are different device classes. It is resolved
once, when the entity is created, because Home Assistant records the device class
in the entity registry; swapping the physical probe means reinstalling.

Two parameters are handled with a documented caveat: `inbat` is the only battery
whose scale the API does not state and stays a 0/1 flag, and `t5lst` has no unit
(the vendor example uses 9999) so it is read as minutes with 9999 meaning
"nothing recorded".

Also replaces the now-stale TODO block in const.py - it listed exactly these
sensors as unimplemented - and documents the complete coverage in the README.
2026-07-26 15:20:07 +02:00
SchiZzA 5e4b02fd6e
fix: address remaining review findings across forwarders and utils
Forwarders (Windy / Pocasi):
- `enabled` becomes a property reading the option live. Toggling it does not
  reload the entry, so the cached copy left the diagnostics sensor reporting a
  stale value indefinitely for a disabled forwarder.
- Reject empty credentials explicitly: an empty string is still a `str`, so
  `checked` alone let an unconfigured forwarder send a request that could only
  ever be refused.
- Use `persistent_notification.async_create` instead of the sync variant.
- Use WINDY_MAX_RETRIES consistently; add POCASI_CZ_MAX_RETRIES.

Utils:
- `voc_level_to_text` / `battery_5step_to_pct` go through `to_int` like every
  other value_fn. A bare `int()` raised on a garbage payload value, which
  `WeatherSensor.native_value` then logged with a full traceback on every push.
  Out-of-range battery steps are clamped to a valid percentage.
- Simplify `check_disabled` (drop the redundant camelCase flag).

Config flow:
- `retain_data` re-reads SENSORS_TO_LOAD at submit time. Auto-discovery appends
  to it from the webhook handler, so writing back the snapshot taken when the
  step opened silently rolled back any sensor discovered meanwhile.

Coordinator:
- Drop the per-push staleness recalculation; the hourly timer already covers it.

Ecowitt:
- Tolerate aioecowitt moving/renaming the internal SENSOR_MAP rather than
  failing the whole module import; unit-variant dedup degrades instead.

Translations:
- Battery state key `unknown` -> `drained`: the entity emits UnitOfBat.UNKNOWN,
  whose value is "drained", so the old key never matched and the raw state
  leaked into the UI. cs.json was already correct.
- Remove the dead `migration` step; `async_step_migration` does not exist.

Also: remove verified-dead constants/helpers and fix ~30 docstring typos.
2026-07-25 21:47:54 +02:00
SchiZzA 4dcd76da62
test: align suite with v2.0 runtime_data architecture
The suite predated the coordinator extraction and the typed runtime_data
migration: 5 modules failed to import and 18 tests failed.

- Import IncorrectDataError/WeatherDataUpdateCoordinator from .coordinator.
- Point received_data monkeypatch targets at custom_components.sws12500.coordinator.*
- Stub config entries with async_on_unload + runtime_data (SWSRuntimeData).
- Rewrite test_data, test_sensor_platform and test_integration_lifecycle off the
  removed ENTRY_* hass.data keys onto runtime_data (route dispatcher reuse, ecowitt
  POST route, no hass.data pop on unload, health first_refresh mocked).
- Update config_flow tests for the user menu (pws step) and the options menu
  gaining wslink_port_setup.
- Fix VOCLevel.EXCELENT typo and stale t9 expectations (tuple battery list,
  HCHO int coercion, connection-gated subset).

Result: 168 passed, 0 collection errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 21:47:51 +02:00
SchiZzA 93fd85a487
Add HCHO / VOC air-quality sensors (T9 module)
Support the WSLink t9 air-quality module:
  - t9hcho (formaldehyde, ppb),
  - t9voclv (VOC level 1-5 -> ENUM state)
  - t9bat (0-5 battery -> percentage).

  The t9hcho/t9voclv/t9bat values are dropped when
  t9cn reports the module as disconnected,
  so no empty entities are created.

  - const: HCHO/VOC/T9_BATTERY keys, VOCLevel enum +
  VOC_LEVEL_MAP, BATTERY_NON_BINARY, CONNECTION_GATED_SENSORS,
  REMAP_WSLINK_ITEMS entries
  - utils: voc_level_to_text(),
  battery_5step_to_pct(), connection gating
  - sensors_wslink: HCHO / VOC / T9_BATTERY entity
  descriptions
  - translations (en, cs): hcho, voc (+ states),
  t9_battery
  - tests: tests/conftest.py +
  tests/test_t9_air_quality.py
2026-05-11 23:55:36 +02:00