This release is mainly Refactoring code with minor bug fixes.
Why Refactor? think of it like cleaning the kitchen while you cook. You aren't changing the menu or serving a new dish; you're just making sure the workspace is organized so you don't trip over a drawer or lose the salt when things get busy.
Architecture
- Extracted API layer into api/ package — http_client.py, websocket_client.py, exceptions.py with re-exports from init.py
- ldata_service.py now focuses on data orchestration; transport concerns are isolated
API call reduction (57,600 → ~2,880 calls/day for v2 setup and ~5,760 calls/day for v1 setups)
- Even though most calls (since Ldata v2.x) were really lightweight, why send it if we don't need to
- REST poll loop is now breaker-only — no more bandwidth toggle or CT fetch every 10s
- CT poll auto-disables when WS delivers CT energy counters (detected from both IotCt and IotWhem payloads)
- WS heartbeat interval moved to const.py (WS_HEARTBEAT_INTERVAL) and set to 60s (was 10s)
- New refresh_panel_data REST call at 60s for panel-level data (rssi, voltage, frequency) that WS delivers infrequently
- CT poll loop renamed to "slow poll" — handles panel refresh (always) + CT energy (only when WS doesn't deliver it)
New entities / Options
- Breaker Daily Import sensor — only created for breakers with non-zero import (solar detection: skipped when consumption > 0 and import == 0)
- Blink LED switch per breaker — diagnostic category, controllable via PUT /ResidentialBreakers/{id} with {"blinkLED": true/false}, always created (not gated on breaker control)
- Breaker ID shown in device info via serial_number field
- Added new "Log Raw Websocket String" option
Bug fixes
- Fixed daily import on non-solar breakers falling back to power×time when HW_COUNTER_NONE_TOLERANCE hit — now holds at zero when lifetime import is 0
- Hardcoded sensor.grid_power_total_daily_energy entity reference removed from panel daily energy
- ClientConnectionResetError on WS subscription sends — _ws_send_subscriptions now guards ws.closed and catches connection resets cleanly
- Fire-and-forget heartbeat task now tracked and cancelled on disconnect
- Panel hardware field now uses panel_type instead of hardcoded "LDATA"
- Added CT clamp channel # to Device info an log warnings if not configured
- Clarified naming of "Three Phase" option as it was misleading.
- Clarified naming of "Log Full Websocket Data" to "Log All Parsed Data" option as it was misleading.
Entity cleanup
- All listener registrations moved from init to async_added_to_hass across all sensor, binary sensor, and switch classes
- Breaker Status binary sensor moved to Diagnostic category
- Cloud Connected sensor moved to DIAGNOSTIC group
- BLE RSSI sensor has 5 dBm deadband filter to reduce DB writes
- Removed unused _LEG2_POSITIONS from const.py
Code quality
- Consistent aiohttp.ClientTimeout(total=N) across all HTTP calls
- Lazy % logging in coordinator (was f-strings)
- Logger name consistency — API modules use LOGGER_NAME from const.py instead of hardcoded strings
- Silent except Exception: pass handlers replaced with _LOGGER.debug throughout http_client.py and ldata_service.py