Skip to content

feat(deye-svk): migrate deye-svk 0.2.0 from Blixt L1 registry - #94

Merged
frahlg merged 7 commits into
mainfrom
migrate/deye-svk
Aug 19, 2026
Merged

feat(deye-svk): migrate deye-svk 0.2.0 from Blixt L1 registry#94
frahlg merged 7 commits into
mainfrom
migrate/deye-svk

Conversation

@davmoz

@davmoz davmoz commented Aug 18, 2026

Copy link
Copy Markdown
Member

Migration of a Blixt L1 control driver into the catalog. Depends on #89 (spec).

Driver: Deye three-phase hybrid (Modbus RTU) — lean single-read SvK fast-poll variant; Remote Mode control path verbatim from deye@2.4.4.

Source: the Blixt L1 driver source, version 0.2.0.

Catalog-conformance changes (none to the control path — arm/setpoint/safe-revert/slew/self-heal untouched):

  • PROTOCOL + DRIVER header above the existing DRIVER_MANIFEST. Community tier; manifest control: true; control_enabled stays false until the control-v2 gates (lease, HIL, v2 entrypoints) are met.
  • Canonical W / inverter.W emit keys (Blixt L1 reads both spellings; headroom keys per spec: Blixt L1 headroom emit keys + inverter as reporting stream #89).
  • Poll reads go through a bounded per-block retry: after 3 misses in a row a block is left alone and re-probed periodically instead of costing a failed read every poll. Blocks come straight back when they answer (test_absent_register_settles).
  • No battery emit when nothing answered — the previous 0 W / 0 % on a failed read was a fabricated reading.
  • Portable write-result check (spec: Blixt L1 headroom emit keys + inverter as reporting stream #89): Blixt L1 raises on a refused write, FTW returns an error string; the driver now sees both.
  • driver_default_mode (the unprompted, timer-driven revert) stops reissuing a write the device has refused 3× in a row and logs once (test_refused_write_settles); the explicit deinit command always attempts the revert, and any successful write resets the counter.

make check green.

Safety

Control driver, v1 entrypoints. Safe revert = existing deinit_safe_revert (unchanged). No new write paths. control_enabled: false.

Package or promotion evidence

Control enabled: false. No package in this PR. Control activation is a separate change with default-mode, lease-expiry and HIL evidence.

David Mozart added 5 commits August 18, 2026 10:18
Blixt L1 battery/hybrid drivers report their AC stage on
host.emit("inverter", ...) and feed available_charge_W /
available_discharge_W (battery) and available_import_W /
available_export_W (inverter) into the arbitrator clamp. L1 reads
those keys by exact name with no lowercase fallback, and the profile
already names Blixt as the emit-key reference — but the four keys were
missing from emit_keys.canonical, and the emit-type tests rejected
"inverter" outright because it is not a DER type.

Add the keys to the profile, document that inverter is a stream (the
device is still declared as ders: [battery]), and let the structure /
manifest-consistency tests accept the stream without listing it in
ders. No driver changes; the emit-fields tests already derive their
allowed set from the profile.

Signed-off-by: David Mozart <david@sourceful-labs.com>
Blixt L1's host.write is FC16 with count=1 (Deye ignores FC06 on some
ranges) and exposes host.write_fc06 for true FC06; FTW's is FC06. The
two hosts also disagree on how a failed write is reported: FTW returns
an error string and never raises, Blixt returns true or raises. The
test harness follows FTW, so a Blixt-only driver that trusts pcall's ok
reads a mocked refusal as success. Document both, add write_fc06 to the
profile's modbus group, and give the portable check.

Signed-off-by: David Mozart <david@sourceful-labs.com>
test_signed_manifest_rejects_unsafe_runtime_fields mutated
payload.drivers[0] and relied on it being read-only. The catalog is
sorted by id, so the first control driver whose id sorts before
abb_meter (any id starting with a digit) made the mutations hit a
different, earlier check than the one under test. Select a read-only
modbus.read-only entry explicitly.

Signed-off-by: David Mozart <david@sourceful-labs.com>
konja-261-svk.lua + its manifest were staged by an over-broad git add;
they belong to #95, not here.

Signed-off-by: David Mozart <david@sourceful-labs.com>
Source: https://novacore-devnet.sourceful.dev/device-support/drivers/deye-svk/0.2.0 (byte-identical to blixt-gateway l1/drivers/deye-svk.lua).

Catalog conformance changes, none touching the control path:

- PROTOCOL + DRIVER header above the existing DRIVER_MANIFEST
  (sdm630/foxess pattern); community tier, control: true in the
  manifest, control_enabled stays false until the v2 gates pass.
- Emit canonical W / inverter.W keys (Blixt L1 reads both spellings).
- Poll reads go through a bounded per-block retry: after 3 misses in a
  row a block is left alone and re-probed periodically instead of
  costing a failed read every poll for the life of the session — the
  host counts each failed read against the poll. Blocks come straight
  back when they answer, so a bus hiccup cannot blind self-heal.
- No battery emit when nothing answered: the previous 0 W / 0 % on a
  failed read was a fabricated reading (the same trap the SoC hold
  already existed for).
- Portable write-result check (spec/host-api.md): Blixt L1 raises on a
  refused write, FTW returns an error string; the driver now sees both.
- driver_default_mode (the unprompted, timer-driven revert path) stops
  reissuing a write the device has refused 3 times in a row and logs
  once; the explicit deinit command always attempts the revert, and any
  successful write resets the counter.

Signed-off-by: David Mozart <david@sourceful-labs.com>
Base automatically changed from spec/blixt-l1-emit-keys to main August 19, 2026 07:12
@frahlg
frahlg merged commit 6e85f9f into main Aug 19, 2026
6 checks passed
@frahlg
frahlg deleted the migrate/deye-svk branch August 19, 2026 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants