Skip to content

Commit 968d21d

Browse files
TypQxQAbardenniangregory
authored
v.1.2.0 (#299)
v.1.2.0 introduces Sigenergy Modbus 2.8 compability. It has not been tested with older firmware but is designed for backwards compability. It also adds AC/DC charger fixes. * Fix AC charger switch state by using numeric system state values (#284) The AC charger system state is stored internally as a numeric enum value, not a mapped string. The previous switch logic compared the state against string values ("Initializing", "Fault", etc.), which caused the switch to never correctly reflect the charger state. This change updates is_on_fn to rely on the numeric system state values reported by the charger. Currently treating states 3, 4, and 5 as ON. This aligns the AC charger switch behavior with how other switches (e.g. plant_start_stop) determine their state. * Add VPP Scheduling EMS Work Mode (#289) * Add VPP_SCHEDULING mode to modbus register definitions * Add VPP Scheduling to EMS work modes * Add Modbus 2.8 support * Change modes for plant_hvrt_mode and plant_lvrt_mode * updated to prevent accidental commands when the system state is unknown. * One error handling. * inverter_output_type change * Fix native_unit_of_measurement and device_class * a1 * a2 * I have analyzed the issue and identified the likely cause: the integration was creating new Modbus clients upon connection errors or timeouts without ensuring that the previous client/socket was closed. This "leak" caused an accumulation of ESTABLISHED TCP sessions on the backend, eventually leading to the Home Assistant process hanging or the Modbus server refusing new connections. Additionally, the async_close method (used during integration reload) was skipping closure for clients marked as causing connection errors (self._connected[key] == False). This meant that "broken" but open sockets were left behind during a reload, explaining why only a full HA restart would fix the issue. I have implemented the following fixes in custom_components/sigen/modbus.py : Updated _get_client : Added a check to explicitly close any existing client instance for the given host/port before creating a new one. This prevents leaking the old client when replacing it. Enhanced Error Handling in _get_client : Added an explicit close call if the initial connect() attempt fails, ensuring we don't return a half-open or leaked client on failure. Refactored async_close : Modified the cleanup method to force-close all tracked clients regardless of their internal "connected" flag status. This ensures that even "failed" clients are properly cleaned up when the integration is unloaded or reloaded. These changes should resolve the reported issue of Modbus/TCP session hangs and the inability to recover via integration reload. * feat: introduce switch platform for controlling Sigenergy plant, inverters, and chargers * feat: Introduce switch platform for Sigenergy ESS, providing control over plant, inverter, AC charger, and DC charger states. * 1.2.0a4 * Change "Consumed Power" to use new modbus register and fallback on calculations when not avaliable. * Fix Sigenergy energy flow dashboard, update version and change bug report template. --------- Co-authored-by: Abardenn <33287271+Abardenn@users.noreply.github.com> Co-authored-by: iangregory <iangregory@users.noreply.github.com>
1 parent 8104cd6 commit 968d21d

File tree

21 files changed

+1792
-1311
lines changed

21 files changed

+1792
-1311
lines changed

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: "Bug Report"
22
description: "Report a problem with Sigenergy-Local-Modbus"
33
title: "[Bug] "
4-
labels: ["bug", "more info needed"]
4+
# labels: ["bug", "more info needed"]
55
assignees: []
66

77
body:

.github/copilot-instructions.md

Lines changed: 27 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,35 @@
1-
## General
1+
## Sigenergy Local Modbus (sigen) – AI Coding Notes
22

3-
- Use **async/await** throughout; avoid blocking calls and synchronous I/O inside coroutines.
4-
- Prioritize **concise responses under 1000 characters**.
5-
- Use **spaces** for indentation, with a width of **4 spaces**.
6-
- Use **double quotes** in YAML and JSON.
7-
- Never expose sensitive data in logs or diagnostics.
8-
- Use **camelCase** for variable names.
9-
- Explain your reasoning before providing code.
10-
- Focus on code **readability** and **maintainability**.
11-
- Prioritize using the **most common library** in the community.
3+
- Integration lives in custom_components/sigen/ and follows ConfigEntry + DataUpdateCoordinator.
4+
- Core flow: config_flow.py → __init__.py (setup entry) → modbus.py (SigenergyModbusHub) → coordinator.py → platform entities.
125

13-
## Home Assistant Best Practices
6+
### Domain model
7+
- A “Plant” represents one Modbus TCP endpoint (host/port). Current config flow assumes one plant per HA instance.
8+
- Plant default slave id is 247; device ids are 1–246 for inverters/chargers.
9+
- DC charger data is read “via inverter” (see config_flow.py + sensor.py/switch.py device_info via_device relationships).
1410

15-
- Follow HA's **entity model**, **async APIs**, and **config flow** patterns.
16-
- Use **voluptuous** schemas for YAML configs (`CONFIG_SCHEMA`, `PLATFORM_SCHEMA`).
17-
- Prefer **DataUpdateCoordinator** for async data fetching.
18-
- Raise `ConfigEntryNotReady` or `PlatformNotReady` on setup failures to enable retries.
19-
- Fire events with `hass.bus.async_fire("<domain>_event", {...})`, include `device_id`.
20-
- Listen for events via `async_track_state_change`, `async_track_template_result`, or `hass.bus.async_listen`.
21-
- Use **platform files** (`sensor.py`, `switch.py`, etc.), avoid monolithic modules.
22-
- Support **multiple plants, inverters, AC/DC chargers** dynamically.
23-
- Avoid MQTT or REST polling; use **Modbus TCP** and **DataUpdateCoordinator**.
11+
### Modbus + polling
12+
- Keep all I/O async; avoid adding new blocking calls (especially inside coordinator updates).
13+
- SigenergyModbusHub in modbus.py caches AsyncModbusTcpClient per (host, port) and guards reads/writes with per-connection asyncio locks.
14+
- Register reads are optimized by probing supported registers and building contiguous read intervals (max ~123 registers/read).
15+
- Maintain pymodbus compatibility via the safe wrapper that handles slave/device_id arg changes.
2416

25-
## Modbus TCP
17+
### Entities + naming
18+
- Use SigenergyEntity (sigen_entity.py) as the base and create entities through generate_sigen_entity() (common.py) like existing platform files do.
19+
- Use device_name (not numeric id) as the primary inverter identifier across coordinator/entity layers.
20+
- PV strings are modeled as sub-devices under the inverter; follow sensor.py’s PV device_info pattern.
2621

27-
- Assume **TCP protocol** with slave IDs **1-246**.
28-
- Use **non-blocking** Modbus communication.
29-
- Support multiple devices via config flow or YAML.
22+
### Writes / controls safety
23+
- All writes must go through SigenergyDataUpdateCoordinator.async_write_parameter() (coordinator.py); do not call Modbus write methods directly from entities.
24+
- Respect read-only behavior (hub.read_only) and keep “dangerous” controls disabled by default (entity_registry_enabled_default=False).
3025

31-
## File Structure & Naming
26+
### Calculated sensors
27+
- Calculated/integration sensors are in calculated_sensor.py; they may use Recorder history via executor jobs—keep history work off the event loop.
28+
- If you add new calculations, prefer pure functions in SigenergyCalculations and reuse safe_decimal/safe_float (common.py).
3229

33-
- Follow [HA integration structure](https://developers.home-assistant.io/docs/creating_integration_file_structure):
34-
- `custom_components/<domain>/`
35-
- Include: `manifest.json`, `__init__.py`, `config_flow.py`, `diagnostics.py`, `system_health.py`, `services.yaml`, platform files, `coordinator.py`.
36-
- Tests in `tests/components/<domain>/` with `__init__.py`, `conftest.py`, `test_*.py`.
37-
- Use **HA naming conventions** for entities, sensors, services.
30+
### Diagnostics + privacy
31+
- diagnostics.py must redact host/credentials/serials/MACs via async_redact_data; never log secrets.
3832

39-
## Manifest.json
40-
41-
- Must include: `domain`, `name`, `version` (custom only), `codeowners`.
42-
- Optional: `dependencies`, `after_dependencies`, `requirements`, `iot_class`, `quality_scale`, `ssdp`, `zeroconf`, `bluetooth`, `usb`, `dhcp`.
43-
- Add `"config_flow": true` if UI config supported.
44-
45-
## Config Flow & UI
46-
47-
- Subclass `ConfigFlow`, define async steps, handle reauth, migration, subentries.
48-
- Scaffold config flows, translations, tests from [example repo](https://github.com/home-assistant/example-custom-config).
49-
- Use `async_redact_data` to redact sensitive info in diagnostics.
50-
- Register system health via `async_register` in `system_health.py`.
51-
52-
## Services
53-
54-
- Define service schemas in `services.yaml`.
55-
- Register entity services with `async_register_entity_service`.
56-
57-
## Development Workflow
58-
59-
- Use the **integration scaffold script** (`python3 -m script.scaffold integration`) to generate new components.
60-
- Start with the **minimum integration**: `DOMAIN` constant and `async_setup` returning `True` if init succeeds.
61-
- Keep code modular, readable, and maintainable.
62-
- When adding examples, prefer **HA YAML snippets** or **Python code** compatible with HA.
63-
64-
## Summary
65-
66-
- Write **async, non-blocking, modular** code.
67-
- Follow **HA architecture, naming, and file structure**.
68-
- Use **voluptuous** for validation.
69-
- Prefer **scaffolded** components.
70-
- Support **dynamic multi-device setups**.
71-
- Never leak sensitive data.
72-
- Keep instructions **concise and precise**.
73-
74-
## Project Context & Environment
75-
76-
- **Integration Code:** The core Python code for this Sigenergy integration resides in `q:/Sigenergy-Local-Modbus/custom_components/sigen/`.
77-
- **Home Assistant Instance:** The target Home Assistant instance is running at `http://192.168.1.47:8123/`.
78-
- **Log File:** The Home Assistant log file can be found at `q:/home-assistant.log`.
79-
- **Database:** The Home Assistant database is located at `Q:/home-assistant_v2.db`.
80-
81-
## Available MCP Tools
82-
83-
You have access to specific tools for interacting with the Home Assistant environment:
84-
85-
- **`Home Assistant` MCP Server:** Provides tools (like `HassTurnOn`, `HassTurnOff`, `get_home_state`, etc.) to directly interact with entities and services on the HA instance at `http://192.168.1.47:8123/`. Use this for controlling devices or retrieving state information.
86-
- **`homeassistant_sigen_sql` MCP Server:** Provides tools (`read_query`, `write_query`, etc.) to query or modify the Home Assistant database located at `Q:/home-assistant_v2.db`. Use this for direct database access when needed.
87-
88-
# Rules for Interacting with the Home Assistant Database
89-
90-
This document outlines the structure of the Home Assistant database and provides guidance on using the `homeassistant_sigen_sql` MCP tool for querying it. The database stores historical data about events, states, statistics, and system runs.
91-
92-
## MCP Tool: `homeassistant_sigen_sql`
93-
94-
- **Location:** `Q:/home-assistant_v2.db`
95-
- **Tools:**
96-
- `read_query`: Execute `SELECT` statements.
97-
- `write_query`: Execute `INSERT`, `UPDATE`, `DELETE` statements (use with caution).
98-
- `list_tables`: List all tables in the database.
99-
- `describe_table`: Get the schema for a specific table.
100-
101-
## Key Database Tables
102-
103-
The database normalizes data across several tables to save space. Key tables include:
104-
105-
- **`events`**: Records events fired within Home Assistant (excluding `state_changed`). Links to `event_data` and `event_types`.
106-
- `event_type_id`: Foreign key to `event_types.event_type_id`.
107-
- `time_fired_ts`: Timestamp (Unix float) when the event occurred.
108-
- `data_id`: Foreign key to `event_data.data_id`.
109-
- `context_id_bin`: Binary identifier linking related events/states.
110-
- **`event_data`**: Stores the JSON payload (`shared_data`) for events. Referenced by `events.data_id`.
111-
- **`event_types`**: Stores unique event type strings (`event_type`). Referenced by `events.event_type_id`.
112-
- **`states`**: Records entity state changes. Links to `states_meta` and `state_attributes`.
113-
- `metadata_id`: Foreign key to `states_meta.metadata_id` (identifies the entity).
114-
- `state`: The actual state value (e.g., 'on', 'off', temperature).
115-
- `attributes_id`: Foreign key to `state_attributes.attributes_id`.
116-
- `last_updated_ts`: Timestamp (Unix float) of the last update (state or attributes).
117-
- `last_changed_ts`: Timestamp (Unix float) of the last state *value* change. **NULL** if only attributes changed. Use `COALESCE(last_changed_ts, last_updated_ts)` or equivalent for the actual last change time.
118-
- `old_state_id`: Foreign key linking to the previous state record in the `states` table.
119-
- `context_id_bin`: Binary identifier linking related events/states.
120-
- **`state_attributes`**: Stores the JSON attribute payload (`shared_attrs`) for states. Referenced by `states.attributes_id`.
121-
- **`states_meta`**: Stores unique entity IDs (`entity_id`). Referenced by `states.metadata_id`.
122-
- **`statistics`**: Long-term (hourly) aggregated statistics for sensors.
123-
- `metadata_id`: Foreign key to `statistics_meta.id`.
124-
- `start_ts`: Timestamp (Unix float) for the beginning of the hour.
125-
- `mean`, `min`, `max`: For measurement sensors.
126-
- `state`, `sum`, `last_reset_ts`: For metered sensors (e.g., energy).
127-
- **`statistics_short_term`**: Short-term (5-minute) statistics snapshots. Similar structure to `statistics`. Purged periodically.
128-
- **`statistics_meta`**: Metadata for statistics (`statistic_id`, `unit_of_measurement`, etc.). Referenced by `statistics.metadata_id` and `statistics_short_term.metadata_id`.
129-
- **`recorder_runs`**: Tracks Home Assistant start/stop times.
130-
- `start`, `end`: Timestamps of the run.
131-
- `closed_incorrect`: Boolean indicating if HA did not shut down gracefully. If `true`, the `end` time might not be accurate.
132-
133-
## Context
134-
135-
- `context_id_bin`, `context_user_id_bin`, `context_parent_id_bin` are present in `events` and `states` tables.
136-
- These binary fields link events and state changes that result from a single trigger (e.g., an automation run, a user action).
137-
- Use `hex()` (SQLite/MariaDB) or similar functions to view these IDs in a readable format.
138-
139-
## Querying Best Practices
140-
141-
- **Joins:** Always join related tables (e.g., `states` with `states_meta` and `state_attributes`) to get complete information.
142-
- **Timestamps:** Fields ending in `_ts` are Unix timestamps (float). Use database functions like `DATETIME(..., 'unixepoch', 'localtime')` (SQLite), `from_unixtime()` (MariaDB), or `to_timestamp()` (PostgreSQL) for human-readable times.
143-
- **`last_changed_ts`:** Remember to handle `NULL` values when querying `states.last_changed_ts`.
144-
- **`state_changed` Events:** These are not directly in the `events` table. Query the `states` table for this information. You can `UNION ALL` `events` and `states` queries if needed.
145-
- **Normalization:** Be aware that `event_data`, `event_types`, `state_attributes`, and `states_meta` store unique values referenced by IDs in the main `events` and `states` tables.
146-
147-
## Example Query (SQLite - Get recent state changes for a specific entity)
148-
149-
```sql
150-
SELECT
151-
DATETIME(s.last_updated_ts, 'unixepoch', 'localtime') as last_updated,
152-
sm.entity_id,
153-
s.state,
154-
sa.shared_attrs as attributes,
155-
hex(s.context_id_bin) as context_id
156-
FROM states s
157-
JOIN states_meta sm ON s.metadata_id = sm.metadata_id
158-
LEFT JOIN state_attributes sa ON s.attributes_id = sa.attributes_id
159-
WHERE sm.entity_id = 'sensor.your_entity_id_here'
160-
ORDER BY s.last_updated_ts DESC
161-
LIMIT 10;
162-
```
33+
### CI / repo checks
34+
- This repo is validated by hassfest and HACS Action workflows (.github/workflows/).
35+
- Type checking uses pyright “basic” (pyrightconfig.json); Reference/ is intentionally excluded.

0 commit comments

Comments
 (0)