Repository navigation
fix(zap): read the meter DER under Zap's data model v2 local API - #208
Merged
Merged
Conversation
Some Zap gateway firmware serves the local API in data model v2 (srcful-data-models 2.3.0): every meter field gets an "_ac" suffix and the L1/L2/L3 phase prefix is lowercased (W -> W_ac, L1_V -> l1_V_ac; Hz is unchanged). emit_meter read the v1 names unconditionally, so a Zap on v2 firmware gave FTW no meter reading at all. Add raw_field(), which tries the v1 name, then the _ac suffix, then the lowercased-prefix _ac variant -- no explicit version branch needed. Scoped to the meter DER only; pv/battery have the identical assumption against v2's _ac/_dc split and are left to srcfl#170, since this site runs read_pv/read_battery both false and I have no v2 hardware with those enabled to verify against. Fixes (partially) srcfl#170. Signed-off-by: Marcus Murwall <marcus@murwall.se>
Contributor
Author
|
@frahlg Noticed yesterday that my ZAP received an OTA that changed naming and some other things. |
Member
|
Verified before merge, through FTW's own Lua host (master be11e360) against real Zaps on firmware 7.0.3, GET only:
Thanks for the fix and the evidence. |
This was referenced Nov 4, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(zap): read the meter DER under Zap's data model v2 local API
Fixes (partially — meter DER only) #170.
Problem
A Zap gateway on firmware serving the data model v2 local API
(
srcful-data-models2.3.0,"version":"v2"in/api/devices/<sn>/data/json)gives FTW's
zap.luano meter reading at all.emit_meterreadsraw.Wandthe
meter_fieldstable's v1 names (L1_W,L1_V,L1_A,total_import_Wh,total_export_Wh) directly; none of those keys exist in a v2 payload, sonumber(raw.W)isnil,emit_meterreturnsfalse, and the driver logsZap: site-meter payload unavailableevery poll untilMETER_FAIL_REDISCOVERis hit and discovery invalidates — forever, since the rediscovered payload
has the same shape. A site using Zap as
is_site_meter: trueloses its gridimport/export reading entirely, silently, until someone notices stale
consumption data in the UI.
Reproduced on
platform: srcful-zap-p1), gateway firmware7.0.3(firmwareBuild: v7.0.3-dirty). (Firmware version, model and thev2 field names are the evidence; the gateway's serial, IP and MAC are
redacted here per CONTRIBUTING.md.)
sourceful-zap, local pin at version3.1.0); confirmed the same read-path bug exists unchanged at this repo'scurrent
main(zap.luaidzap, version3.1.1).GET /zap.logg) confirms the HTTP exchangeitself succeeds on every poll (
GET /api/devices/<sn>/data/json→returning device data, 200), and a directcurlof that endpointreturns a well-formed
meterobject — the break is entirely inemit_meter's field names, not in discovery, connectivity, or payloadavailability.
GET /api/devices)ders[].typefor the P1meter is unaffected by the v2 rollout — still
"meter", not renamed likepv→solar— sodiscover_devices()needed no change for this fix.Values compared
zap.lua(v1)WW_ac2328HzHz(unchanged)null(grid frequency not populated by this meter)L1_V/L1_A/L1_Wl1_V_ac/l1_A_ac/l1_W_ac233.8/2.3/447L2_V/L2_A/L2_Wl2_V_ac/l2_A_ac/l2_W_ac233.4/2.6/607L3_V/L3_A/L3_Wl3_V_ac/l3_A_ac/l3_W_ac231.9/5.5/1273total_import_Whtotal_import_Wh_ac58203578total_export_Whtotal_export_Wh_ac10630697Live-site values, MAC/serial redacted. This exactly matches the mapping
#170 already documents from the data model spec — this PR is the
implementation of that issue's "What zap.lua needs" item 1 and 2, scoped to
the meter DER.
Fix
emit_meternow reads every field through a smallraw_field(raw, name)helper: try the exact v1 name, then
name .. "_ac", then (for the L1/L2/L3fields) the lowercased-prefix
_acvariant. Noversionbranch is needed —each lookup just tries progressively more v2-shaped keys, so the same code
path reads either firmware generation.
Hzneeds no fallback since it isunchanged between schemas.
Out of scope (tracked by #170, not done here)
pv→solarrename in bothdiscover_devices()and the data envelope.emit_pv/emit_batteryhave the identical.W/rated_power_W/etc.v1-only field reads against v2's
_ac/_dc-split names. Not fixed here —I don't have hardware with
read_pv/read_batteryenabled on v2 firmwareto verify against, and the DC/AC split for those DERs is a genuinely
different mapping than the meter's (a battery's own measurement is DC;
see zap: read the Zap's data model v2 local API (pv → solar, W → W_ac, ...) #170's table), not just an added suffix.
null-vs-0rule (44 drivers emit numbers they never read (null, never 0) #169) — not touched;number()already treats aJSON
nullas absent (tonumber(nil)isnil), so this fix doesn'tregress it, but I haven't separately verified 44 drivers emit numbers they never read (null, never 0) #169 end-to-end.
Testing
raw_fieldfallback chain was derived from, andverified against, a real Zap gateway on firmware
7.0.3(v2 payload) —confirmed
emit_meternow returnstrueand the meter DER emits with thepatched driver swapped in locally (FTW
custom-drivers/zap.luabind mountoverride, ahead of this PR existing). Not verified against v1 firmware
hardware — no Zap still on v1 to test the regression path against; the
v1 branch is exact-name lookup, unchanged from before this PR, and is also
covered by the
v1test scenario below.source (
bash tools/build_luac.sh --with-interpreter):./luac55 -p drivers/lua/zap.lua— parses clean.bash tools/check_sandbox.sh— all 92 drivers pass, zap included../lua55 drivers/tests/lua_harness/test_zap_meter_schema.lua v1— exit 0../lua55 drivers/tests/lua_harness/test_zap_meter_schema.lua v2— exit 0.Both scenarios assert
emit_meterproduces the identical normalizedreading (
w,l1_w/l2_w/l3_w, phase V/A,freq_hz,total_import_wh/total_export_whand theirimport_wh/export_whaliases) from a v1-named and a v2-named (
_ac-suffixed, lowercase phaseprefix) mock payload. Also checked
DRIVER.id/version/host_api_*/read_onlymatch what the manifest bump below produced.python tools/bump_driver.py --id zap --level patch,python tools/sync_manifests.py,python tools/generate_index.py,python tools/generate_devices.py,python tools/generate_support_status.py,and
python tools/validate_manifest.py(plainpython3, notuv run— nouv/package install available where this was drafted, but these toolsonly import the standard library and this repo's own
manifest_parser)all ran clean:
92 manifests checked, 0 errors,92 manifests match their sourceon a second pass (idempotent),index.yaml/devices.yaml/SUPPORT_STATUS.md/support-status.jsonregenerated and included.uv run/make bootstrapitself (nouvwhere this wasdrafted — the above tools were run directly with
python3instead, whichworked since none of them need third-party packages), and the
signed channel accepts this treecheck, which needs CI's signinginfrastructure and can't be run outside it.
Changes in this PR
drivers/lua/zap.lua:raw_field()helper +emit_meterfix, headercomment note,
DRIVER.version3.1.1→3.1.2.manifests/zap.yaml:version: "3.1.2",sha256/size_bytesregeneratedfor real via
sync_manifests.py. Did not changetested_devices[].notesor
firmware_versions— open to adding a v1/v2 compatibility note thereif a maintainer wants it, wasn't sure what convention to follow.
index.yaml,devices.yaml,SUPPORT_STATUS.md,support-status.json:regenerated to match (mechanical, from the above tools).
CHANGELOG.md:[Unreleased]entry added.drivers/tests/lua_harness/test_zap_meter_schema.lua+drivers/tests/test_zap_meter_schema.py: new test pair (see Testing).Bumped as
LEVEL=patch— same registers and fields, no new telemetry — butLEVEL=minoris defensible too given it's compatibility with a whole newAPI generation rather than a narrow register fix; happy to re-bump if a
maintainer prefers minor.
Why only the meter DER
This FTW site runs Zap strictly as
is_site_meter: truewithread_pv/read_batterybothfalse(PV and battery come from theinverter/battery's own native drivers), so the meter path is what broke and
what I could verify end-to-end against real v2 hardware. Filing this as a
focused fix rather than attempting the full #170 scope blind.