Skip to content

Commit c22dd13

Browse files
FireSonclaude
andcommitted
docs: map existing codebase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 28f6246 commit c22dd13

File tree

7 files changed

+1479
-0
lines changed

7 files changed

+1479
-0
lines changed

.planning/codebase/ARCHITECTURE.md

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Architecture
2+
3+
**Analysis Date:** 2026-03-27
4+
5+
## Pattern Overview
6+
7+
**Overall:** Home Assistant Integration using Coordinator Pattern with Device Abstraction Layer
8+
9+
**Key Characteristics:**
10+
- Home Assistant ConfigEntry-based integration with async coordinator
11+
- Device polymorphism (Legacy MQTT vs. ZenSDK BLE/Cloud protocols)
12+
- Entity-driven model where devices create platform-specific entities (sensors, numbers, switches, etc.)
13+
- Manager orchestration of power distribution and charging strategy across multiple devices
14+
- Multi-protocol connectivity: Cloud MQTT, Local MQTT (legacy), BLE (ZenSDK)
15+
16+
## Layers
17+
18+
**Coordinator Layer (ZendureManager):**
19+
- Purpose: Periodic update coordinator for Home Assistant integration, manages global power distribution logic
20+
- Location: `custom_components/zendure_ha/manager.py`
21+
- Contains: `ZendureManager` class extending `DataUpdateCoordinator[None]`
22+
- Depends on: Api, ZendureDevice, FuseGroup, EntityDevice
23+
- Used by: Home Assistant core, platform modules (sensor, number, select, switch, binary_sensor, button)
24+
- Responsibilities: Device lifecycle, power balancing, p1meter integration, operation mode management (OFF, MANUAL, MATCHING, etc.)
25+
26+
**Device Layer (ZendureDevice):**
27+
- Purpose: Represents physical Zendure hardware devices with protocol-specific implementations
28+
- Location: `custom_components/zendure_ha/device.py` (base), `custom_components/zendure_ha/devices/*.py` (device-specific)
29+
- Contains: `ZendureDevice` (base), `ZendureLegacy` (MQTT protocol), `ZendureZenSdk` (BLE/Cloud protocol), `ZendureBattery`
30+
- Depends on: Entity layer, sensor/number/select/switch/binary_sensor modules
31+
- Used by: ZendureManager, Api
32+
- Responsibilities: MQTT/BLE publish-subscribe, power command execution (charge/discharge), property updates, battery management
33+
34+
**Battery Abstraction (ZendureBattery):**
35+
- Purpose: Represents attachable battery modules within devices
36+
- Location: `custom_components/zendure_ha/device.py`
37+
- Contains: `ZendureBattery` subclass of `EntityDevice`
38+
- Depends on: EntityDevice
39+
- Used by: ZendureDevice
40+
- Responsibilities: Model inference from serial number, capacity tracking, integration into parent device
41+
42+
**Entity Layer (EntityDevice, EntityZendure):**
43+
- Purpose: Base abstractions for Home Assistant entity creation and property tracking
44+
- Location: `custom_components/zendure_ha/entity.py`
45+
- Contains: `EntityDevice` (device container), `EntityZendure` (entity base mixin)
46+
- Depends on: Home Assistant helpers (device_registry, entity_registry, restore_state)
47+
- Used by: ZendureDevice, ZendureManager, all platform entities
48+
- Responsibilities: Device info creation, entity registry management, property-to-entity mapping, state persistence
49+
50+
**Platform Layer:**
51+
- Purpose: Home Assistant platform implementations extending EntityZendure
52+
- Location: `custom_components/zendure_ha/{sensor,number,select,switch,binary_sensor,button}.py`
53+
- Contains: `ZendureSensor`, `ZendureNumber`, `ZendureSelect`, `ZendureSwitch`, `ZendureBinarySensor`, `ZendureButton`
54+
- Depends on: EntityZendure, Home Assistant platform classes
55+
- Used by: Home Assistant entity/component registry
56+
- Responsibilities: Platform-specific behavior, value rendering, state updates, callbacks
57+
58+
**API & Transport Layer (Api):**
59+
- Purpose: MQTT client management, device factory, connection handling
60+
- Location: `custom_components/zendure_ha/api.py`
61+
- Contains: `Api` class with static MQTT clients, device creation registry
62+
- Depends on: paho-mqtt, bleak, device classes
63+
- Used by: ZendureManager, config flow
64+
- Responsibilities: Cloud/local MQTT connection setup, device instantiation by model name, authentication
65+
66+
**Fuse Group Layer (FuseGroup):**
67+
- Purpose: Power distribution constraints across multiple devices
68+
- Location: `custom_components/zendure_ha/fusegroup.py`
69+
- Contains: `FuseGroup` class managing multiple `ZendureDevice` instances
70+
- Depends on: ZendureDevice
71+
- Used by: ZendureManager power balancing
72+
- Responsibilities: Device-level power limit calculation under fuse constraints, weighted distribution
73+
74+
**Config Flow (Setup):**
75+
- Purpose: User configuration and authentication setup
76+
- Location: `custom_components/zendure_ha/config_flow.py`
77+
- Contains: `ZendureConfigFlow`, `ZendureOptionsFlowHandler`
78+
- Depends on: Api, const definitions
79+
- Used by: Home Assistant config entry flow
80+
- Responsibilities: Token validation, MQTT settings collection, P1 meter entity selection
81+
82+
## Data Flow
83+
84+
**Integration Setup:**
85+
86+
1. User adds integration via config flow (CONF_APPTOKEN required)
87+
2. `async_setup_entry()` in `__init__.py` creates `ZendureManager`
88+
3. `ZendureManager.loadDevices()` calls `Api.Connect()` to authenticate and fetch MQTT credentials
89+
4. Devices are instantiated from factory in `Api.createdevice` based on product model
90+
5. Platforms forward-loaded (sensor, number, select, switch, binary_sensor, button)
91+
6. `ZendureManager.async_config_entry_first_refresh()` triggers first coordinator update
92+
93+
**Device Message Flow:**
94+
95+
1. MQTT message received on `iot/{prodkey}/{deviceId}/properties/read` topic
96+
2. `ZendureDevice.mqttProperties()` parses JSON payload
97+
3. Property key matched against entity names via `EntityDevice.createEntity` mapping
98+
4. `ZendureDevice.entityUpdate(key, value)` called
99+
5. Entity's `update_value(value)` updates state and triggers listeners
100+
6. Aggregate sensors (energy, switch counts) accumulate via `aggregate()` method
101+
102+
**Power Distribution Flow (Manager Mode):**
103+
104+
1. `ZendureManager.async_update_data()` runs at SCAN_INTERVAL (60s)
105+
2. `update_operation()` reads current operation mode (OFF, MANUAL, MATCHING, STORE_SOLAR)
106+
3. P1 meter power polled from Home Assistant state
107+
4. `calculate_power()` determines optimal charge/discharge per device
108+
5. `FuseGroup.charge_limit()` / `discharge_limit()` applies fuse constraints
109+
6. Device-specific `charge(power)` or `discharge(power)` methods invoked via MQTT
110+
7. Next update scheduled with SmartMode intervals (TIMEFAST=2.2s, TIMEZERO=4s)
111+
112+
**State Persistence:**
113+
114+
1. RestoreEntity subclasses (`ZendureRestoreSensor`, `ZendureRestoreNumber`, `ZendureRestoreSelect`) maintain state
115+
2. Home Assistant restore_state module persists data across restarts
116+
3. On startup, previous values loaded and set before first coordinator update
117+
118+
## Key Abstractions
119+
120+
**EntityDevice:**
121+
- Purpose: Container for device metadata, entities dictionary, platform-agnostic device representation
122+
- Examples: `ZendureDevice`, `ZendureManager`, `ZendureBattery`
123+
- Pattern: Base class providing device info (name, model, serial, manufacturer), entity tracking dict
124+
125+
**EntityZendure:**
126+
- Purpose: Mixin providing common entity behavior (unique_id generation, device_info property, property name mapping)
127+
- Examples: All platform entities inherit from both `EntityZendure` and Home Assistant entity type
128+
- Pattern: snakecase property name → translation key conversion, entity registry integration
129+
130+
**Device Polymorphism:**
131+
- Purpose: Support different communication protocols and feature sets per device type
132+
- Examples: `ZendureLegacy` (MQTT-only, older devices), `ZendureZenSdk` (cloud + BLE, newer ZenSDK protocol)
133+
- Pattern: Template method pattern where device subclasses override `charge()`, `discharge()`, `mqttProperties()`
134+
135+
**CreateEntity Mapping:**
136+
- Purpose: Dynamic entity creation from property definitions
137+
- Examples: `createEntity` dict maps property names to (unit, device_class, state_class) tuples
138+
- Pattern: Configuration-driven entity instantiation, allows extensibility without code changes
139+
140+
## Entry Points
141+
142+
**Integration Setup:**
143+
- Location: `custom_components/zendure_ha/__init__.py::async_setup_entry()`
144+
- Triggers: User adds integration via config flow, Home Assistant service calls
145+
- Responsibilities: Create ZendureManager, load devices, setup platforms, register update listener
146+
147+
**Config Flow:**
148+
- Location: `custom_components/zendure_ha/config_flow.py::ZendureConfigFlow.async_step_user()`
149+
- Triggers: User selects "Zendure" in add integration UI
150+
- Responsibilities: Collect token, validate authentication, persist config
151+
152+
**Coordinator Update:**
153+
- Location: `custom_components/zendure_ha/manager.py::ZendureManager.async_update_data()`
154+
- Triggers: Scheduled at SCAN_INTERVAL (60s), can be forced via coordinator methods
155+
- Responsibilities: Update operation mode, check P1 meter, calculate power distribution, invoke device commands
156+
157+
**Platform Setup:**
158+
- Location: `custom_components/zendure_ha/{platform}.py::async_setup_entry()`
159+
- Triggers: After platforms are forward-loaded by integration
160+
- Responsibilities: Register entity add callback for platform
161+
162+
## Error Handling
163+
164+
**Strategy:** Defensive async exception handling with logging
165+
166+
**Patterns:**
167+
- Try-catch blocks in entity update handlers catch malformed values or type mismatches
168+
- `_LOGGER.error()` with traceback for unexpected exceptions
169+
- MQTT connection failures logged but don't prevent platform initialization
170+
- Device offline state set to 0 (DeviceState.OFFLINE) on connection loss
171+
- Invalid config entry returns False from `async_setup_entry()` to prevent loading
172+
173+
## Cross-Cutting Concerns
174+
175+
**Logging:** All modules use `logging.getLogger(__name__)` pattern, debug-level MQTT message logging controlled by CONF_MQTTLOG flag
176+
177+
**Validation:**
178+
- Token validation in `Api.Connect()` returns None on auth failure
179+
- Entity update handlers validate value types before processing (e.g., int conversion)
180+
- MQTT payload parsed as JSON with error logging on decode failure
181+
182+
**Authentication:**
183+
- App token passed in config entry data, used by Api to authenticate cloud MQTT connection
184+
- MQTT user/password from config_flow for local MQTT (legacy devices)
185+
- BLE connection uses bleak-retry-connector for reliability
186+
187+
---
188+
189+
*Architecture analysis: 2026-03-27*

0 commit comments

Comments
 (0)