Two long-standing rough edges with Ecowitt running alongside the SWS device:
- Dedup (#1): aioecowitt emits both metric and imperial sensors per quantity
(tempc/tempf, rainratemm/rainratein, ...). Only the imperial twin is mapped to the
SWS sensors, so the metric twin (and other unmapped extras) showed up as duplicate
native entities. _on_new_sensor now skips a key whose unit-variant twin is already
mapped or already created (twin groups derived from aioecowitt's SENSOR_MAP). HA
converts units via device_class, so a single variant suffices.
- Translation (#2): native sensors used the raw English aioecowitt name. Common
single-instance families (rain rate / hourly / event / 24h / weekly / monthly /
yearly / total rain, absolute pressure, feels-like, indoor dew point, CO2) now use
curated translation_keys (added to strings/en/cs); long-tail / multi-channel
sensors keep the English name fallback.
- Also add the imperial RAIN_RATE_INCHES / RAIN_COUNT_INCHES to STYPE_TO_HA so those
native rain sensors get a device class and unit.
Device unification (one "WeatherHub" device + station-type sensor) stays for the
rename round, as agreed.
314 passing, 100% coverage; ruff + basedpyright clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- S8: received_data now rejects empty configured credentials (IncorrectDataError)
and present-but-empty incoming credentials (HTTPUnauthorized) instead of relying
solely on the config flow. Credential validation extracted into
_validate_credentials() (also resolves the C901 complexity warning and unifies
the WU/WSLink branches).
- S6: cap auto-created native Ecowitt entities (MAX_NATIVE_ECOWITT_SENSORS) to bound
entity-registry growth from a fabricated multi-key payload.
- S5: store only the exception class name in Windy/Pocasi last_error (surfaced via
entity attributes; str(ex) could embed the request URL).
- S7: verified safe - the native-sensor INFO log is parametrized (%s), so no
format-string injection; left as-is.
308 passing, 100% coverage; ruff + basedpyright clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- C1: received_data / received_ecowitt_data return 503 (not 500) when the entry is
mid-reload (runtime_data gone); add_new_sensors / add_new_binary_sensors are
defensive no-ops in that window too.
- C2/C3: Windy and Pocasi reserve their next send window *before* the await, so
concurrent webhooks can no longer both pass the rate-limit check and double-send
(which could falsely trip the auto-disable threshold).
- C5: EcowittBridge.set_add_entities flushes unmapped sensors parsed before the
platform was ready (aioecowitt fires new_sensor_cb only once per key).
- C6: a forwarder auto-disabling itself from the hot path no longer forces a full
config-entry reload (update_listener now skips reload for live-read flags
SENSORS_TO_LOAD / WINDY_ENABLED / POCASI_CZ_ENABLED).
- C7: to_int accepts decimal-formatted integers ("180.0").
- C8: forwarders use dt_util.utcnow() (UTC-aware) instead of naive datetime.now().
Tests updated; 305 passing, 100% coverage; ruff + basedpyright clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>