Home Assistant custom component that integrates EG4 devices (inverters, GridBOSS, batteries) with Home Assistant via local Modbus TCP, WiFi dongle, cloud API, or hybrid connectivity. Supports multi-station architecture with comprehensive device hierarchy and individual battery management.
PLATINUM TIER COMPLIANT - Meeting all 36 requirements (3 Platinum + 5 Gold + 10 Silver + 18 Bronze)
Platinum Tier Requirements (3/3):
- Async Dependency: Full async implementation using aiohttp for all HTTP operations
- Websession Injection: API client supports injected aiohttp.ClientSession from Home Assistant
- Strict Typing: Comprehensive mypy strict typing configuration with type hints throughout codebase
GOLD TIER COMPLIANT - Meeting all 33 requirements (5 Gold + 10 Silver + 18 Bronze)
Gold Tier Requirements (5/5):
- Translation Support: Complete i18n infrastructure with
strings.jsonandtranslations/directory - UI Reconfiguration:
async_step_reconfigure()andasync_step_reconfigure_plant()flows for credential/station updates - User Documentation: Comprehensive README with troubleshooting, FAQ, and automation examples
- Automated Tests: Full test coverage with
test_config_flow.py,test_reconfigure_flow.py, and tier validation scripts - Code Quality: Enterprise-grade implementation with proper error handling, logging, and type hints
Silver Tier Requirements (10/10) - Inherited:
- Service exception handling with
ServiceValidationError - Config entry unload support
- Complete configuration documentation
- Entity availability management
- Integration owner specification (@joyfulhouse)
- Unavailability logging
MAX_PARALLEL_UPDATESin all platforms- UI-based reauthentication flow
- Test coverage >95% target
- Installation documentation
Validation:
python tests/validate_platinum_tier.py # All 3 Platinum requirements
python tests/validate_gold_tier.py # All 5 Gold requirements
python tests/validate_silver_tier.py # All 10 Silver requirements
python tests/validate_bronze_tier.py # All 18 Bronze requirements
pytest tests/ --cov=. --cov-report=term-missing
mypy --config-file mypy.ini . # Strict type checkingQuality Scale Reference: https://www.home-assistant.io/docs/quality_scale/ Platinum Tier Reference: https://developers.home-assistant.io/docs/core/integration-quality-scale/#-platinum
- Base URL:
https://monitor.eg4electronics.com - Authentication:
/WManage/api/login(POST) - 2-hour session with auto-reauthentication - Serial Format: 10-digit numeric strings (e.g., "1234567890")
Station/Plant (plantId)
└── Parallel Group (min:0, max:n)
├── MID Device (GridBOSS) (min:0, max:1)
└── Inverters (min:1, max:n)
└── Batteries (min:0, max:n)
Station Discovery:
/WManage/web/config/plant/list/viewer(POST) - List available stations/plants
Device Discovery:
/WManage/api/inverterOverview/getParallelGroupDetails(POST) - Parallel group hierarchy/WManage/api/inverterOverview/list(POST) - All devices in station
Runtime Data:
/WManage/api/inverter/getInverterEnergyInfoParallel(POST) - Parallel group energy/WManage/api/inverter/getInverterRuntime(POST) - Inverter runtime metrics/WManage/api/inverter/getInverterEnergyInfo(POST) - Inverter energy data/WManage/api/battery/getBatteryInfo(POST) - Battery details and individual battery array/WManage/api/midbox/getMidboxRuntime(POST) - GridBOSS/MID device data
The config flow uses a single EG4ConfigFlow class with menu-based navigation.
Connection type (http/local/hybrid) is auto-derived from configured data, not chosen upfront.
Directory Structure (config_flow/):
__init__.py— Unified EG4ConfigFlow class (~920 lines)discovery.py— Device auto-discovery via Modbus/Dongleschemas.py— Voluptuous schema buildershelpers.py— Utility functions (unique IDs, migration, etc.)options.py— EG4OptionsFlow for interval configuration and data validation toggle
- Entry Menu: User picks "Cloud (HTTP)" or "Local Device"
- Cloud Path: Credentials → Station Selection → (optional) Add Local Device → Finish
- Local Path: Pick Modbus or Dongle → Enter connection details → Auto-discover device → Add more or finish
- Connection Type: Auto-derived — cloud-only=
http, local-only=local, both=hybrid
- Entry Point:
reconfigure_menu(MENU type) - Options: Update cloud credentials, add/remove local devices, detach cloud
- Preserves existing entity IDs and automations
_derive_connection_type(has_cloud, has_local)→ http/local/hybrid_validate_cloud_credentials()→ shared error handling for auth_store_cloud_input(user_input)→ saves cloud form data to flow statebuild_unique_id(mode, ...)→ unique ID generation per modeformat_entry_title(mode, name)→"{BRAND_NAME} - {name}"(mode parameter unused)
- Unique ID:
{serial}_{data_type}_{sensor_key}_{batteryKey?} - Entity ID (Inverter):
eg4_{model}_{serial}_{sensor_name} - Entity ID (Battery):
eg4_{model}_{serial}_battery_{batteryKey}_{sensor_name} - Entity ID (GridBOSS):
eg4_gridboss_{serial}_{sensor_name}
Standard Inverters (FlexBOSS21, FlexBOSS18, 18kPV, 12kPV, XP):
- Full sensor set: power, voltage, current, energy, temperature
- Individual battery device creation
- Runtime, energy, and battery data endpoints
GridBOSS MID Devices:
- Grid management sensors only (no batteries)
- Grid interconnection, UPS, load management
- Smart load ports, AC coupling, generator integration
Individual Batteries:
- Voltage, current, power, SoC, SoH
- Temperature, cycle count, cell voltages
- Cell voltage delta (imbalance monitoring)
- Concurrent API Calls:
asyncio.gather()for parallel device data fetching - Session Caching: 2-hour session reuse with auto-reauthentication
- Smart Caching: Differentiated TTL by data volatility:
- Device Discovery: 15 minutes
- Battery Info: 5 minutes
- Parameters: 2 minutes
- Quick Charge: 1 minute
- Runtime/Energy: 20 seconds
- Cache Invalidation: Pre-hour boundary clearing for date rollover protection
- Circuit Breaker: Exponential backoff for API failures
- API calls can return data for multiple devices - fetch once, update all relevant sensors
- Parallel updates with
MAX_PARALLEL_UPDATESlimits - Different update intervals for different data types
Release notes should follow the CHANGELOG.md format. See CHANGELOG.md for detailed release history.
- v3.2.0 — Major release: unified config flow, data validation, split-phase sensors, BMS diagnostics, 779 tests, pylxpweb>=0.9.26
- See
CHANGELOG.mdfor full history
- Container:
homeassistant-dev - Docker Compose:
/Users/bryanli/Projects/joyfulhouse/homeassistant-dev/docker-compose.yaml - Image:
homeassistant/home-assistant:latest(Python 3.13) - Port: 8123 → http://localhost:8123
eg4_web_monitor integration:
./eg4_web_monitor/custom_components/eg4_web_monitor → /config/custom_components/eg4_web_monitor
pylxpweb library (editable install):
../python/pylxpweb/src/pylxpweb → /usr/local/lib/python3.13/site-packages/pylxpweb
Code changes are live-mounted. Restart container to pick up Python import changes:
docker restart homeassistant-dev
docker logs -f homeassistant-dev # Check for errorsThree separate HA config directories allow testing each connection mode in isolation:
| Mode | Config Directory | Purpose |
|---|---|---|
cloud |
./config |
Baseline/reference - all data validated against this |
local |
./config-local |
Local-only (Modbus TCP or WiFi Dongle) |
hybrid |
./config-hybrid |
Local polling + cloud supplemental data |
Switch modes using the helper script:
cd /Users/bryanli/Projects/joyfulhouse/homeassistant-dev
./scripts/eg4-switch-mode.sh cloud # Switch to cloud mode
./scripts/eg4-switch-mode.sh local # Switch to local mode
./scripts/eg4-switch-mode.sh hybrid # Switch to hybrid modeCheck current mode:
grep ":/config" docker-compose.yaml | head -1
# Returns: - ./config:/config OR - ./config-local:/config OR - ./config-hybrid:/configSetup details:
- All configs share the same HA user accounts/UI (copied from
./config) - Each config has EG4 integration entry removed for fresh configuration
- Integration titles: Cloud/Hybrid use plant name (e.g., "EG4 Electronics - 6245 N WILLARD"), Local uses custom name
- Only ONE mode runs at a time to avoid API rate limits and Modbus collisions
Validation requirements:
- Cloud mode is baseline - all data should match production
- Local mode must have ALL entities present in cloud (minimum parity)
- Hybrid mode should poll locally with cloud supplemental data
- Allow small margin of error for live readings due to cloud lag
pylxpweb import errors (e.g., "cannot import name 'LuxpowerClient'"):
cd /Users/bryanli/Projects/joyfulhouse/python/pylxpweb
git restore src/pylxpweb/ # Restore accidentally deleted source files
docker restart homeassistant-devIntegration not loading: Check docker logs homeassistant-dev for import errors
Changes not reflecting: Container restart required for Python imports
This project uses uv for dependency management. Tests run from the repository root:
# Run all tests (692 tests)
uv run pytest tests/ -x --tb=short
# Run with coverage
uv run pytest tests/ --cov=custom_components/eg4_web_monitor --cov-report=term-missing
# Run specific test file
uv run pytest tests/test_config_flow.py -v# 1. Lint and format
uv run ruff check custom_components/ --fix && uv run ruff format custom_components/
# 2. Type checking
uv run mypy --config-file tests/mypy.ini custom_components/eg4_web_monitor/
# 3. All tests
uv run pytest tests/ -x
# 4. Tier validation scripts
uv run python tests/validate_silver_tier.py
uv run python tests/validate_gold_tier.py
uv run python tests/validate_platinum_tier.pytest_config_flow.py— Cloud onboarding, menu navigation, error handling (56 tests)test_reconfigure_flow.py— Reconfigure menu, credential updates (24 tests)test_config_flow_helpers.py— Utility functions (unique IDs, timezone, migration)test_coordinator.py— Data update coordinator (120+ tests)test_coordinator_http.py— HTTP flow, error handling, station data (19 tests)test_coordinator_local.py— Modbus params, local device data, transport (23 tests)test_sensor_entities.py— Sensor entity creation, feature filtering (42 tests)test_update_entities.py— Firmware update entity lifecycle (38 tests)test_options_flow.py— Options flow, data validation toggleconftest.py— Shared fixtures (mock stations, mock API client)
- pytest-homeassistant-custom-component for HA-specific fixtures
enable_custom_integrationsfixture auto-enabled in conftest.py- Coverage target: >95% for production code
- Use
/WManage/api/inverterOverview/listwithplantIdfiltering - Extract
batteryKeyfromgetBatteryInfofor individual battery sensors - Detect GridBOSS devices and apply MID-specific sensor sets
- Implement 2-hour session caching with auto-reauthentication
- Use concurrent API calls for performance
- Multi-station support: one integration instance per station
- Device hierarchy: inverters with individual battery sensors
- GridBOSS special handling: grid management sensors only
- Battery entity IDs: use
batteryKeyfor uniqueness - Data separation: inverter status vs individual battery data
- All imports present and properly managed
- Comprehensive exception handling with logging
- Type hints throughout codebase
- Smart caching to minimize API calls
- Test coverage for all features in CI pipeline
- Use
time.monotonic()instead of deprecatedasyncio.get_event_loop().time() - TypedDict for configuration dictionaries (e.g.,
SensorConfiginconst.py) - Proper
DeviceInfo | Nonereturn types for device info methods
This integration follows Python string formatting best practices:
-
F-Strings (Preferred): Use for all non-logging string formatting
# Good - Modern and readable message = f"Device {serial} has {count} sensors" entity_id = f"sensor.{model}_{serial}_{sensor_type}"
-
Percent Formatting (Logging Only): Use for logging to enable lazy evaluation
# Good - Lazy evaluation improves performance _LOGGER.debug("Processing device %s with type %s", serial, device_type) _LOGGER.error("Failed to fetch data for %s: %s", serial, error)
-
Avoid
.format(): Do not use.format()method# Bad - Outdated style message = "Device {} has {} sensors".format(serial, count)
Rationale:
- F-strings provide better readability and performance for immediate string construction
- Percent formatting in logging provides lazy evaluation (string only built if log level active)
- This dual approach optimizes both code clarity and runtime performance
Base Entity Classes:
The integration provides base entity classes in base_entity.py to eliminate code duplication:
EG4DeviceEntity: Base for all device entities (inverters, GridBOSS, parallel groups)EG4BatteryEntity: Base for individual battery entitiesEG4StationEntity: Base for station/plant level entitiesEG4BaseSensor: Base for device sensors with monotonic value support (inherits from EG4DeviceEntity)EG4BaseBatterySensor: Base for individual battery sensors (inherits from EG4BatteryEntity)EG4BatteryBankEntity: Base for battery bank aggregate sensors (inherits from EG4DeviceEntity)EG4BaseSwitch: Base for all switch entities with optimistic state management
All new entity classes should inherit from these base classes to maintain consistency.
Coordinator Mixins (coordinator_mixins.py):
The coordinator uses a mixin-based architecture for better separation of concerns:
class EG4DataUpdateCoordinator(
DeviceProcessingMixin,
DeviceInfoMixin,
ParameterManagementMixin,
DSTSyncMixin,
BackgroundTaskMixin,
FirmwareUpdateMixin,
DataUpdateCoordinator,
):Each mixin handles a specific responsibility:
DeviceProcessingMixin: Processes device objects and maps properties to sensorsDeviceInfoMixin: Providesget_device_info(),get_battery_device_info(), etc.ParameterManagementMixin: Handles parameter refresh operationsDSTSyncMixin: Manages daylight saving time synchronizationBackgroundTaskMixin: Manages background task lifecycleFirmwareUpdateMixin: Extracts firmware update information
Switch Base Class Pattern:
The EG4BaseSwitch class provides:
- Common entity attributes setup (name, icon, unique_id, entity_id)
- Device data and parameter data helper properties (
_device_data,_parameter_data) - Optimistic state management for immediate UI feedback
_execute_switch_action()helper for standardized switch operations_get_inverter_or_raise()helper for inverter object retrieval
class EG4QuickChargeSwitch(EG4BaseSwitch):
async def async_turn_on(self, **kwargs):
await self._execute_switch_action(
action_name="quick charge",
enable_method="enable_quick_charge",
disable_method="disable_quick_charge",
turn_on=True,
)Integration Not Found:
- Restart HA:
docker-compose restart homeassistant - Check container logs for errors
- Verify file permissions
Authentication Errors:
- Verify credentials
- Check network connectivity to
monitor.eg4electronics.com - Review SSL verification settings
Missing Entities:
- Check device discovery logs
- Verify API responses contain expected data
- Restart integration
Data Not Updating:
- Check coordinator update logs
- Verify API session is valid
- Monitor network connectivity
| Control | Register | Type |
|---|---|---|
| EPS/Battery Backup | 21, bit 0 | Bit field |
| AC Charge Enable | 21, bit 7 | Bit field |
| Forced Charge | 21, bit 11 | Bit field |
| Forced Discharge | 21, bit 10 | Bit field |
| Green/Off-Grid Mode | 110, bit 8 | Bit field |
| PV Charge Power | 64 | 0-100% |
| Discharge Power | 65 | 0-100% |
| AC Charge Power | 66 | 0-100% |
| AC Charge SOC Limit | 67 | 0-100% |
| Charge Current | 101 | Amps |
| Discharge Current | 102 | Amps |
| On-Grid SOC Cutoff | 105 | 10-90% |
| Off-Grid SOC Cutoff | 106 | 0-100% |