Skip to content

Commit 0a50f0d

Browse files
author
Maksym Shtembuliak
committed
feat: expand unsupported device support
Add Sandisolar runtime identification, typed support-acquisition readiness, and fail-closed cloud evidence workflows that remain usable before inverter detection. Document and test the complete active-learning endpoint, command, interception, and recovery consent boundary.
1 parent 62a2c02 commit 0a50f0d

62 files changed

Lines changed: 1948 additions & 259 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,40 @@ the GitHub release body should be rendered from the matching version section her
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added built-in detection and telemetry mapping for the Sandisolar SD 11KP48V
13+
WIFI fingerprint (`layout=4`, `model=0x8003`) from issue #13 evidence.
14+
Compatible controls remain explicitly untested and Full-Control-only.
15+
- Added a typed support-acquisition boundary so read-only cloud evidence can be
16+
collected for an identified collector before an inverter driver is known.
17+
18+
### Changed
19+
20+
- Moved device-support analysis and collector cloud-traffic capture under one
21+
**Expand device support** menu with separate readiness rules for read-only
22+
metadata, active learning, and proxy capture.
23+
- Active-learning consent now explicitly covers the temporary collector
24+
endpoint change, bounded cloud test commands, local interception, and route
25+
restoration.
26+
27+
### Fixed
28+
29+
- Fixed stale generic-SMG projections that could survive a stronger runtime
30+
fingerprint and hide the correct model-specific telemetry surface.
31+
- Added typed per-route probe evidence so support diagnostics can distinguish
32+
framed and raw-serial attempts without exposing endpoints or payloads.
33+
- Kept support evidence and proxy diagnostics available when the collector is
34+
identified but the inverter driver is still unresolved, while active
35+
route-owning operations continue to fail closed.
36+
37+
### Docs
38+
39+
- Added safe instructions for manually testing the unreleased `main` branch
40+
without creating a tag or GitHub release.
41+
- Updated device-learning and proxy-capture guides for the current menu names,
42+
trust boundaries, and recovery behavior.
43+
1044
## [0.3.0-beta.4] - 2026-08-25
1145

1246
### Added

‎README.md‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,23 @@ It is a small ESP8266/ESP32-based bridge that connects directly to the inverter
127127
3. Restart Home Assistant.
128128
4. Add **EyeBond Local** from **Settings → Devices & Services**.
129129

130+
### Testing the unreleased `main` branch
131+
132+
Use this only when a maintainer asks you to test a fix that is not in a release
133+
yet. It does not update through HACS and may change before the next release.
134+
135+
1. Back up your Home Assistant configuration.
136+
2. Download the current [`main` branch archive](https://github.com/groove-max/ha-eybond-local/archive/refs/heads/main.zip).
137+
3. Remove the existing `config/custom_components/eybond_local/` directory, then
138+
copy the complete directory from the archive into `config/custom_components/`.
139+
Do not mix files from two builds.
140+
4. Restart Home Assistant and check the EyeBond Local entries.
141+
5. When reporting a result, include the Git commit shown on the repository page
142+
and attach a new Support Archive.
143+
144+
To return to a published build, reinstall the latest release through HACS or
145+
replace the directory with the complete directory from that release archive.
146+
130147
---
131148

132149
## Setup
@@ -235,8 +252,8 @@ mode, and the difference between unavailable and disabled entities.
235252

236253
## Device learning
237254

238-
Some devices can be added in read-only or partial mode first. **Expand support
239-
for this device** can then collect extra evidence or check which additional
255+
Some devices can be added in read-only or partial mode first. **Expand device
256+
support** can then collect extra evidence or check which additional
240257
settings and sensors your exact device supports.
241258

242259
Use it when:
@@ -247,12 +264,13 @@ Use it when:
247264

248265
What to expect:
249266

250-
1. Start **Configure → Expand support for this device**.
267+
1. Start **Configure → Expand device support**.
251268
2. Choose **Analyze device data** (recommended) or the advanced active-control
252269
verification.
253270
3. When more than one compatible API is available, choose the exact cloud
254271
source for this run.
255-
4. Read the safety notice when active verification is selected.
272+
4. For active verification, confirm the temporary collector endpoint change,
273+
bounded cloud test commands, and their local interception by Home Assistant.
256274
5. Sign in to the supported cloud account for this one session, if requested.
257275
6. Review the result. Read-only evidence does not add entities automatically;
258276
only locally proven active results can be applied.

‎README.uk.md‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,23 @@ PI30 та SRNE. Рівень підтримки залежить від моде
130130
3. Перезапустіть Home Assistant.
131131
4. Додайте **EyeBond Local** через **Налаштування → Пристрої та служби**.
132132

133+
### Тестування неопублікованої гілки `main`
134+
135+
Використовуйте цей спосіб лише тоді, коли розробник просить перевірити виправлення,
136+
якого ще немає в релізі. Така збірка не оновлюється через HACS і може змінитися
137+
до наступного релізу.
138+
139+
1. Створіть резервну копію конфігурації Home Assistant.
140+
2. Завантажте архів поточної [гілки `main`](https://github.com/groove-max/ha-eybond-local/archive/refs/heads/main.zip).
141+
3. Видаліть наявну папку `config/custom_components/eybond_local/`, а потім
142+
скопіюйте туди всю папку з архіву. Не змішуйте файли з різних збірок.
143+
4. Перезапустіть Home Assistant і перевірте записи EyeBond Local.
144+
5. Разом із результатом вкажіть Git commit зі сторінки репозиторію та додайте
145+
новий архів підтримки.
146+
147+
Щоб повернутися до опублікованої версії, перевстановіть останній реліз через
148+
HACS або повністю замініть папку версією з архіву релізу.
149+
133150
---
134151

135152
## Налаштування
@@ -243,7 +260,7 @@ Modbus-пристрої можуть оновлюватися частіше, н
243260
## Навчання пристрою
244261

245262
Деякі пристрої спочатку додаються в режимі лише читання або часткової
246-
підтримки. **Розширити підтримку цього пристрою** може зібрати додаткові дані
263+
підтримки. **Розширити підтримку пристрою** може зібрати додаткові дані
247264
або перевірити, які локальні сенсори й елементи керування підтримує саме ваш
248265
пристрій.
249266

@@ -255,12 +272,13 @@ Modbus-пристрої можуть оновлюватися частіше, н
255272

256273
Що відбувається:
257274

258-
1. Відкрийте **Налаштувати → Розширити підтримку цього пристрою**.
275+
1. Відкрийте **Налаштувати → Розширити підтримку пристрою**.
259276
2. Виберіть **Аналізувати дані пристрою** (рекомендовано) або розширену активну
260277
перевірку елементів керування.
261278
3. Якщо доступно кілька сумісних API, виберіть конкретне хмарне джерело для
262279
цього запуску.
263-
4. Прочитайте попередження, якщо вибрано активну перевірку.
280+
4. Для активної перевірки підтвердьте тимчасову зміну endpoint колектора,
281+
обмежені тестові команди через хмару та їх локальне перехоплення Home Assistant.
264282
5. Увійдіть у підтримуваний хмарний акаунт лише для цього сеансу, якщо це буде
265283
запропоновано.
266284
6. Перегляньте результат. Дані лише для читання не створюють сутності
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"schema_version": 1,
3+
"model_key": "sandisolar_sd_11kp48v_wifi",
4+
"manufacturer": "Sandisolar",
5+
"model": "SD 11KP48V WIFI",
6+
"aliases": [],
7+
"lifecycle": "experimental",
8+
"variants": [
9+
{
10+
"variant_key": "layout4_model32771",
11+
"label": "Protocol-4 fingerprint 171=0x8003, 184=4",
12+
"device_descriptor_keys": [
13+
"sandisolar_sd_11kp48v"
14+
],
15+
"known_firmware": []
16+
}
17+
],
18+
"validation": {
19+
"hardware": "captured",
20+
"telemetry": "partial",
21+
"controls": "none"
22+
},
23+
"coverage": {
24+
"runtime_control_surface": "available",
25+
"smartess_control_surface": "unknown",
26+
"vendor_register_map": "documented",
27+
"notes": [
28+
"The exact layout/model fingerprint is captured from the issue #13 beta.4 support archive.",
29+
"Telemetry uses the documented Anenji protocol-4 register schema; this prevents generic SMG interpretation of register 227 as temperature.",
30+
"The compatible ANJ write surface is inherited only as untested Full-Control candidates. Automatic control mode exposes none of these writes."
31+
]
32+
},
33+
"known_limitations": [
34+
"No local write has been validated on this exact Sandisolar fingerprint; every inherited control remains Full-Control-only until hardware retesting."
35+
],
36+
"knowledge_summary": "Sandisolar SD 11KP48V WIFI with exact layout-4/model-0x8003 identity. Captured telemetry follows the documented ANJ protocol-4 map, while compatible write candidates remain explicitly untested.",
37+
"source_keys": [
38+
"github_issue_13_sandisolar_sd_11kp48v_beta4",
39+
"documentation_anenji_protocol_3_10"
40+
]
41+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"schema_version": 1,
3+
"source_key": "github_issue_13_sandisolar_sd_11kp48v_beta4",
4+
"kind": "github_issue",
5+
"title": "Sandisolar SD 11KP48V WIFI beta.4 support archive",
6+
"reference": "project-issue:13#support-archive-20260825",
7+
"captured_at": "2026-08-25",
8+
"visibility": "public",
9+
"summary": "A beta.4 support archive captured the exact Modbus fingerprint layout 4 and model code 0x8003 for a Sandisolar SD 11KP48V WIFI reported as an ANJ-11KW-compatible inverter. The live values at registers 227 and 231 follow the documented protocol-4 frequency and temperature layout rather than the generic SMG layout.",
10+
"assertions": [
11+
"commercial_identity",
12+
"fingerprint",
13+
"runtime_compatibility",
14+
"telemetry_validation"
15+
]
16+
}

‎custom_components/eybond_local/config_entry.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
CONNECTION_STRATEGIES,
6363
CONNECTION_STRATEGY_CALLBACK_ON_DEMAND,
6464
CONNECTION_STRATEGY_INBOUND,
65-
CONTROL_MODE_READ_ONLY,
65+
DEFAULT_CONTROL_MODE,
6666
DEFAULT_DRIVER_DETECTION_STRATEGY,
6767
DEFAULT_POLL_INTERVAL,
6868
DEFAULT_POLL_MODE,
@@ -359,7 +359,10 @@ async def _async_create_entry_from_result(
359359
CONF_CONNECTION_TYPE: connection_type,
360360
**connection_settings,
361361
CONF_CONNECTION_MODE: stored_connection_mode,
362-
CONF_CONTROL_MODE: CONTROL_MODE_READ_ONLY,
362+
# ``control_mode`` is durable user intent. Runtime driver binding
363+
# supplies the temporary write interlock while inverter detection
364+
# is pending; a new entry must not masquerade as user Read-only.
365+
CONF_CONTROL_MODE: DEFAULT_CONTROL_MODE,
363366
CONF_COLLECTOR_PN: collector_pn,
364367
CONF_DETECTION_CONFIDENCE: "none",
365368
CONF_DETECTED_MODEL: "",
@@ -604,7 +607,7 @@ async def _async_create_manual_entry(
604607
driver_hint=driver_hint,
605608
)
606609
data.setdefault(CONF_CONNECTION_TYPE, connection_type)
607-
data[CONF_CONTROL_MODE] = CONTROL_MODE_READ_ONLY
610+
data[CONF_CONTROL_MODE] = DEFAULT_CONTROL_MODE
608611
collector_capabilities = _result_collector_capabilities(result)
609612
data[CONF_COLLECTOR_IP] = collector_ip
610613
data[CONF_DETECTION_CONFIDENCE] = "none"

‎custom_components/eybond_local/flow_translations/en.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
"action_reload_local_metadata": "Reload local metadata",
66
"action_rollback_local_metadata": "Rollback local metadata",
77
"action_proxy_capture": "Collector traffic capture",
8-
"cloud_tools_title": "Cloud traffic tools",
8+
"cloud_tools_title": "Expand device support",
99
"shadow_learning_status_overlay_activated": "Discovered controls activated for this device and reload requested.",
1010
"shadow_learning_status_support_exported": "Support package exported without running control discovery.",
1111
"shadow_learning_preflight_summary": "Can start: {can_start}; blockers: {blockers}",
12-
"shadow_learning_warning": "Control discovery is advanced and optional. It briefly uses {cloud_provider_label} to test which settings Home Assistant can control. Testing all option values or numeric settings can trigger cloud-side actions and requires explicit preview and confirmation.",
12+
"shadow_learning_warning": "Active verification is advanced and optional. It temporarily reroutes the collector through Home Assistant, asks {cloud_provider_label} to send bounded test commands, and intercepts the matching local writes. It requires explicit confirmation.",
1313
"yes": "Yes",
1414
"no": "No",
1515
"none": "None",
@@ -62,7 +62,7 @@
6262
"cloud_learning_metadata_unit_conflict_detail": "cloud unit {observed_unit}; expected {expected_unit}",
6363
"cloud_learning_metadata_result": "The read-only check found {cloud_metadata_count} metadata field(s), including {cloud_semantic_candidate_count} recognized semantic candidate(s). {cloud_history_summary} No local register mapping was proven, so no entities or controls were added. {cloud_local_history_review_summary} {local_register_observation_summary} Download the support package or return to the menu.",
6464
"cloud_learning_inactive_draft_available": "{count} exact-route candidate(s) can be saved as an inactive local sensor draft for review. This does not add entities.",
65-
"control_discovery_intro_hint": "Home Assistant will briefly sign in to {cloud_provider_label} to find which settings it can control on this device. Your login is used only for this check and is not saved.\n\n⚠️ Before you continue, fully CLOSE the {cloud_app_label} mobile app. If it stays open it competes with this check for the device and can disrupt the scan or interfere with the inverter.\n\nConfirm below to continue.",
65+
"control_discovery_intro_hint": "Home Assistant will temporarily change the collector's cloud endpoint to a local Home Assistant route. The selected cloud service will then send bounded test commands for this device. Home Assistant must capture and block the matching local writes before they reach the inverter; if that interception cannot be proven, the check stops. Afterward Home Assistant restores the previous endpoint. The vendor app may briefly lose live data during the switch; if automatic restoration cannot be confirmed, a recovery action remains available. Your login is used only for this check and is not saved.\n\n⚠️ Before you continue, fully CLOSE the {cloud_app_label} mobile app so it does not compete with the check.\n\nConfirm the temporary routing, test commands, and local interception below.",
6666
"control_discovery_credentials_hint": "Enter your {cloud_provider_label} username and password. They are used only for this one check and are not saved.",
6767
"control_discovery_progress_status": "Checking device information…",
6868
"control_discovery_result_intro": "Discovery finished and the temporary cloud connection is closed. {control_discovery_selected_count} control(s) are turned on. Apply them, download the support package, or return to the menu.",

0 commit comments

Comments
 (0)