Skip to content

feat(catalog): say whether a driver needs the internet, and who has to unlock it - #56

Merged
HuggeK merged 2 commits into
srcfl:mainfrom
HuggeK:driver-connectivity
Jul 31, 2026
Merged

feat(catalog): say whether a driver needs the internet, and who has to unlock it#56
HuggeK merged 2 commits into
srcfl:mainfrom
HuggeK:driver-connectivity

Conversation

@HuggeK

@HuggeK HuggeK commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

protocol names a transport, and answers neither question an owner asks first: does this need the internet, and what do I have to get from the manufacturer before it works at all?

nibe_local and myuplink are both protocol: http, and both read a NIBE heat pump. They are opposites — one reads the pump over the LAN, the other reads NIBE's cloud. The catalog could not tell them apart.

connectivity: local        # local | cloud  — where it talks while running
setup: [device_screen]     # what a human must obtain first, and from whom

Why not http_local / http_online

That was the obvious move and the wrong one. protocol feeds PROTOCOL_PERMISSIONS, so new values change what permissions a package build grants; it doesn't generalise (MQTT can be a cloud broker); and it doubles the Protocol filter. Orthogonal fields compose on the page instead — "HTTP · Local · Enable on the device screen".

The backfill, from what the sources actually do

77 local, 3 cloud. myuplink, easee_cloud and tibber are the only drivers that hardcode a vendor endpoint. Every other HTTP driver builds its base URL from config, and the 51 Modbus, 8 MQTT and 4 serial drivers name a vendor only as homepage.

setup is the second axis, and the more interesting one — a driver can be local and still be locked until somebody unlocks the interface:

Gate Drivers
device_screen nibe_local — the pump's own installer menu; no app, no account
device_ui sonnen (JSON API + token), ctek, ctek_v2, ctek_hybrid (ModbusTCPEnable)
installer sma_pv — "one-time installer step in the WebUI / Sunny Portal"
vendor_portal myuplink, easee_cloud, tibber
bridge heishamon, opendtu, opendtu_mqtt, esphome_dsmr, esphome-dsmr, tesla_vehicle, zuidwijk_p1, goodwe

17 have a recorded gate. 63 do not, and absent means unrecorded — the page renders "Not recorded", never "Nothing to set up", and a driver with none drops out of the setup filter rather than passing as though it needed nothing. Only setup: [none] is a claim, and nothing claims it yet. Same principle as verification_status: report what is known, not what looks complete.

It is checkable, not decorative

tests/test_connectivity.py reads an asymmetry that is hard to fake: a cloud endpoint must be hardcoded, because there is nowhere else for it to come from, while a LAN address arrives from config. So a driver claiming local must build no request against a public hostname, and one claiming cloud must name the service it needs. On the current catalog it separates exactly the three from the other 77 — verified in both directions. A cloud driver must also record what its vendor demands, so nobody meets the account requirement only after installing.

No driver version moves — and why that is not the same call as #53

#55 had to version the ders correction from #53 because ders reaches the published bytes. These two fields do not: ftw_repository._load_channel copies an explicit allowlist into the artifact metadata (ders, protocol, manufacturer, model_family, and the DRIVER table), and connectivity/setup are not in it. So the channel republishes nothing and the release check has nothing to refuse. spec/manifest-v2.md records that, next to the CONTRIBUTING rule it qualifies.

If a host should ever see these fields at install time, that is a deliberate follow-up which does cost a version bump per driver.

Where a bridge ends and a device begins

bridge is a translator that is neither the device nor the vendor's own gateway product: a HeishaMon board, OpenDTU, an ESPHome build, TeslaBleHttpProxy, a serial-to-Ethernet bridge. A gateway the customer buys as part of the system — the Sourceful Zap, a Ferroamp EnergyHub — is the device. esphome_dsmr's own header draws exactly this line against zap.lua, so the rule is the repository's, not mine.

Ferroamp's local MQTT is granted per site on request, which is what vendor_approval exists for, but nothing in the repo states it — so it stays unrecorded rather than asserted.

Verification

  • validate_manifest.py: 80 manifests, 0 errors
  • pytest tests/: 614 passed, 60 skipped (was 293 before this branch; +321 new parametrised cases). One pre-existing Windows-only failure in test_blueprint.py, which invokes bash with a Windows path and fails identically on an untouched tree
  • Page rebuilt with generate_site.py and inspected in the built output: Reach column present, both filter groups rendered (Local only / Needs the internet, and the five gates present), badges and the "To set up" card row in place. devices.yaml and the support status regenerate unchanged

🤖 Generated with Claude Code

…o unlock it

protocol names a transport and answers neither question an owner asks first.
nibe_local and myuplink are both protocol: http and both read a NIBE heat
pump, and they are opposites: one reads the pump over the LAN, the other
reads NIBE's cloud. The catalog could not tell them apart.

Two fields, orthogonal to protocol rather than folded into it. Splitting
protocol into http_local/http_online was the obvious move and the wrong one:
protocol feeds PROTOCOL_PERMISSIONS, so new values change what a package
build grants, and it does not generalise -- MQTT can be a cloud broker too.

  connectivity: local | cloud    where the driver talks while running
  setup: [...]                   what a human must obtain first, and from whom

Backfilled across all 80 drivers from what the sources do. 77 local, 3 cloud:
myuplink, easee_cloud and tibber are the only drivers that hardcode a vendor
endpoint. Every other HTTP driver builds its base URL from config, and the
Modbus, MQTT and serial drivers name a vendor only as homepage.

setup is the second axis, and a driver can be local and still locked until
somebody unlocks the interface: nibe_local is enabled in the pump's own
installer menu -- no app, no account -- while sonnen and CTEK are enabled in
the device's web UI, sma_pv needs an installer, and eight drivers need a
bridge in between. 17 have a recorded gate; 63 do not, and absent means
unrecorded. The page renders that as "Not recorded", never "Nothing to set
up", and only setup: [none] claims a device needs nothing but an address.

tests/test_connectivity.py stops connectivity being decorative. A cloud
endpoint must be hardcoded -- there is nowhere else for it to come from --
while a LAN address comes from config, so the test reads that asymmetry in
both directions and separates exactly the three from the other 77.

Neither field reaches the signed artifact: _load_channel copies an explicit
allowlist and these are not in it. So no driver version moves, unlike the
ders correction in srcfl#53 that srcfl#55 had to version.

Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
This branch was cut before srcfl#48, srcfl#57, srcfl#62 and srcfl#63 landed. Five files
conflicted; all five were additions on both sides and keep both:

  CHANGELOG.md          both wrote under the same ### Added heading
  spec/manifest-v2.md   Connectivity/Setup beside main's DER Types, and
                        the numbered validation list renumbered so the
                        upstream_docs rule survives
  manifests/myuplink.yaml  connectivity/setup from here, ders [heatpump]
                        and 1.1.1 from srcfl#63 -- the empty ders this branch
                        was written against no longer exists
  tools/generate_site.py   Reach labels beside the vendor-document labels
  index.yaml            regenerated rather than hand-merged

Also records V2.4 in the migration ledger. The branch adds two manifest
fields and updated the field table and validation rules, but never
recorded the version the way V2.3 did for upstream_docs.

The claim that neither field reaches the signed artifact is checked
rather than trusted: with all 80 manifests carrying connectivity,
stripping both fields back out moves no artifact digest, so no driver
needs a version bump.

Signed-off-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK merged commit 4b5b474 into srcfl:main Jul 31, 2026
6 checks passed
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.

2 participants