|
1 | | -## General |
| 1 | +## Sigenergy Local Modbus (sigen) – AI Coding Notes |
2 | 2 |
|
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. |
12 | 5 |
|
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). |
14 | 10 |
|
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. |
24 | 16 |
|
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. |
26 | 21 |
|
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). |
30 | 25 |
|
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). |
32 | 29 |
|
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. |
38 | 32 |
|
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