Commit Graph

9 Commits (8b38db69515cfefb72f885dfa324f90a522bee7c)

Author SHA1 Message Date
SchiZzA 793ea1991a
feat(device): merge all entities into one shared device with station-type model
All entities (weather, battery, health, native Ecowitt) now report through a
single device under the existing {(DOMAIN,)} identifier, removing the separate
"Ecowitt {model}" device that appeared whenever Ecowitt was enabled. This
collapses the previous two-device layout (and the duplicate/untranslated native
Ecowitt sensors living on the second device) into one.

The device model reflects the running station type via _station_model():
"Ecowitt <model>" when Ecowitt is active (model learned from the payload's
`model` field and stored on runtime_data.ecowitt_model), else "WSLink", else
"PWS". Device identity is centralised in data.build_device_info() and reused by
every platform.

No device-registry migration is required: the identifier is unchanged; only the
model/grouping change. Full device unification under a renamed hub is deferred
to the rename round.

Tests: device-info assertions updated to the shared device across the entity
suites; new _station_model/build_device_info branch coverage in test_data.py;
received_ecowitt now asserts the learned model. 320 passed, 100% coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 21:47:53 +02:00
SchiZzA 0c9f7fbc68
feat(ecowitt): dedupe unit-variant sensors and translate common native ones
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>
2026-07-25 21:47:53 +02:00
SchiZzA f4da5fdb27
fix(security): harden forwarders, cap native ecowitt entities, reject empty creds
- 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>
2026-07-25 21:47:52 +02:00
SchiZzA 14466cbe60
fix(correctness): reload-window guards, rate-limit races, ecowitt flush, dt_util
- 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>
2026-07-25 21:47:52 +02:00
SchiZzA 2b4e1a5f8c
fix: address review findings across forwarders and helpers
- wind_dir_to_text: treat 0/missing direction as None (calm) so a missing
  wind direction no longer renders as North; azimut lambdas pass None too.
- windy: log response status (was logging an un-awaited coroutine method);
  count "unexpected response" toward the disable threshold even when logging
  is off.
- pocasi: disable threshold >3 -> >=3 to match Windy's 3-strike behavior.
- ecowitt: always attach device_info to native sensors, including unknown
  sensor types (previously left orphaned without a device).
- const: stop remapping WSLink connection flags (t1cn/t234cXcn) into the
  payload; they had no entity and leaked ghost *_connection keys into data
  and persisted SENSORS_TO_LOAD. Gating uses the raw keys and is unaffected.
- config_flow: fix WSLink port fallback typo 433 -> 443; drop mutable default
  argument on async_step_init.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 21:47:51 +02:00
SchiZzA 6363351d9c
fix: coordinator reuse, route dispatcher, binary sensor translations & test suite alignment
- Reuse existing `WeatherDataUpdateCoordinator` instance across reloads instead of
  creating a new one; routes keep pointing to the same coordinator, so entities
  stay subscribed and updates never silently drop
- `update_listener` now skips full reload when only `SENSORS_TO_LOAD` changes
  (auto-discovery); avoids the "frozen UI" window caused by temporary platform unload
- Replaced direct `route._handler` mutation with a proper `Routes` dispatcher;
  all aiohttp routes are registered once and an internal dispatcher decides which
  handler is active — safe to switch at runtime without touching the router
- Fixed `self.config_entry` / `self.config` inconsistency in coordinator

- `BatteryBinarySensor` now carries correct `device_info` (same identifiers as
  `WeatherSensor`) → single device card in UI instead of two
- Translations for binary sensors must live under `entity.binary_sensor.*`,
  not `entity.sensor.*`

- Fixed all tests broken by new `register_path(hass, coordinator, coordinator_h, entry)` signature
- Updated `_RouterStub` to accept `name=` kwargs and return objects with `.method`
- Added `async post()` to `_RequestStub` stubs
- Aligned `Routes` tests with new `method:path` dispatch key format and `show_enabled()` output
- Replaced `WindyApiKeyError` with `WindyPasswordMissing` to match actual exceptions
- Updated `_FakeResponse` to use HTTP status codes instead of response text strings
- Patched `persistent_notification.create` in Windy push tests to avoid `SimpleNamespace` errors
2026-07-25 21:47:50 +02:00
SchiZzA e3d00389de
fix: Fixed type errors 2026-07-25 21:47:50 +02:00
SchiZzA 7025d8e193
Typos corrected. 2026-07-25 21:47:48 +02:00
schizza 9c9bca0c70
Implement Ecowitt protocol support and dynamic binary sensor discovery.
- Integrate aioecowitt to parse incoming weather station payloads and map sensors to the SWS pipeline.
- Add a new webhook endpoint at /weatherhub/{webhook_id} with support for dynamic route resolution.
- Expand battery binary sensor definitions and implement logic for dynamic entity creation.
- Bump version to 2.0.0-pre1.
2026-07-25 21:47:48 +02:00