Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Driver versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html
### Fixed
- **`nibe_local` was filed as a meter.** Its manifest declared `ders: [meter]`; it reads a NIBE S-series heat pump. The driver emits nothing but metrics — `hp_power_w`, `hp_used_power_w`, the temperatures and the lifetime energy counters, all through `host.emit_metric` and never through `host.emit("meter", …)` — which its header states is deliberate, so that a pump's electrical draw cannot double-count against the site's real grid meter. The catalog was advertising a DER the driver does not provide, and the published page (#51) badges and filters on exactly that field: the pump sat under **Meter** and was absent from **Heat pump**, where an owner looking for it would start. Now `ders: [heatpump]`, matching `heishamon` — the other metric-only heat pump, which has been filed that way since it landed. The Lua source and its `sha256` are untouched, so the driver a host runs is byte-for-byte the one it ran before
- **`nibe_local`** 1.1.0 → 1.1.1 — **the version has to move anyway, because the signed artifact carries this metadata.** `ders` is not catalog-only decoration: `_load_channel` copies it into the artifact's `capabilities` and the channel publishes source and metadata as one signed file, so correcting the field changed the published bytes. The channel then refused the release — `error: nibe_local: changed artifact needs a higher version than 1.1.0` — and it was right to, since that immutability is what lets an operator name the bytes a site is running. A patch rather than a minor: no register, field or emitted metric changed, only the DER the catalog states. The `DRIVER` table is deliberately left alone, keeping the driver byte-identical to its FTW baseline the way #29 established
- **`myuplink` was filed under no device type at all.** Its manifest declared `ders: []`, so the one MyUplink cloud heat-pump driver was absent from **Heat pump** and from every other filter on the catalog page — unreachable by device type, which is how an owner looks for their pump. It is now `ders: [heatpump]`, matching the other two heat pumps. This revisits a deliberate choice: when #27 widened the DER vocabulary it set `myuplink` to none, reading `ders` as the emit stream a driver produces rather than the device it covers. #53 then settled the question the other way for `nibe_local`, citing `heishamon` — and the three drivers are the same case. All three report a pump through `host.emit_metric` and never call `host.emit`, `myuplink` and `heishamon` emit the same `hp_*` metric names, and two of the three already declared `heatpump`. `myuplink` was the one left behind, not the one holding the line
- **`myuplink`** 1.1.0 → 1.1.1 — the version has to move, for the reason `nibe_local` 1.1.1 did: `ders` is copied into the signed artifact's `capabilities`, so this edit changes the published bytes even though no Lua moved. Unlike #53 the channel never got the chance to refuse it — the `signed channel accepts this tree` check answered on the pull request, and putting the version back to 1.1.0 there reproduces `myuplink: changed artifact needs a higher version than 1.1.0 (published)`. A patch: no register, parameter or emitted metric changed, only the device type the catalog files it under. The `DRIVER` table is left alone, keeping the driver byte-identical to its FTW baseline the way #29 established
- **`spec/manifest-v2.md` documented four DER types where the validator accepts seven.** `ev`, `heatpump` and `vehicle` have been valid since #27 and are used by seven manifests, but the field table a driver author reads never listed them — so the spec silently disagreed with `tools/validate_manifest.py`, and the vocabulary that was missing is exactly the one `myuplink` needed. The table is now complete and says what the field means for a metrics-only driver, which is the ambiguity that produced three different answers for three drivers doing the same thing. It also records that `ders` reaches the signed artifact, so changing it costs a version

### Added
- **A published driver catalog at [srcfl.github.io/device-drivers](https://srcfl.github.io/device-drivers/)** — all 80 drivers, searchable by manufacturer or model number and filterable by device type, protocol, tier and control. `tools/generate_site.py` builds it from the manifests and the Lua sources on every push to `main`, so no copy of this data is checked in and the page cannot fall behind what a host installs. It reports hardware evidence the way the repository does rather than the way a catalog usually does: 5 drivers state they have been confirmed against physical hardware and 28 state that they have not, and `tests/test_site.py` fails the build if a driver's `verification_status` is ever rendered as a stronger claim than the driver makes
Expand Down
4 changes: 2 additions & 2 deletions SUPPORT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ Catalog source is not proof that a target can install or run a driver.
| kstar | 1.1.1 | blixt-l1 | not_assessed | — | — | not_recorded | — | not_assessed | no |
| mennekes | 1.0.3 | ftw-core | not_assessed | — | — | not_recorded | — | not_assessed | no |
| mennekes | 1.0.3 | blixt-l1 | not_assessed | — | — | not_recorded | — | not_assessed | no |
| myuplink | 1.1.0 | ftw-core | not_assessed | — | — | not_recorded | — | not_assessed | no |
| myuplink | 1.1.0 | blixt-l1 | not_assessed | — | — | not_recorded | — | not_assessed | no |
| myuplink | 1.1.1 | ftw-core | not_assessed | — | — | not_recorded | — | not_assessed | no |
| myuplink | 1.1.1 | blixt-l1 | not_assessed | — | — | not_recorded | — | not_assessed | no |
| nibe_local | 1.1.1 | ftw-core | not_assessed | — | — | not_recorded | — | not_assessed | no |
| nibe_local | 1.1.1 | blixt-l1 | not_assessed | — | — | not_recorded | — | not_assessed | no |
| opendtu | 1.0.2 | ftw-core | not_assessed | — | — | not_recorded | — | not_assessed | no |
Expand Down
4 changes: 2 additions & 2 deletions devices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -963,8 +963,8 @@ manufacturers:
protocols:
- protocol: http
driver: "myuplink"
version: "1.1.0"
ders: []
version: "1.1.1"
ders: [heatpump]
control: true
firmware_versions: ""
notes: "Read-only heat-pump telemetry via MyUplink Cloud REST API v2: compressor power + hot-water/indoor/outdoor temperatures. Observe-only — no control. OAuth: authorization-code + refresh-token (connect in Settings → Devices)."
Expand Down
4 changes: 2 additions & 2 deletions index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,10 @@ drivers:
size_bytes: 3766
sha256: "5f985b8917aea7b08fba13c02506b45e40232a763393dc6af7c6eeb6ab5853af"
- name: "myuplink"
version: "1.1.0"
version: "1.1.1"
tier: core
protocol: http
ders: []
ders: [heatpump]
control: true
size_bytes: 15278
sha256: "d754fd4af96c2047f9d9e458a776ad8f10510569836618f4631dd70e2510f404"
Expand Down
4 changes: 2 additions & 2 deletions manifests/myuplink.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: "myuplink"
version: "1.1.0"
version: "1.1.1"
tier: core
author: "Sourceful Labs AB"
protocol: http
ders: []
ders: [heatpump]
control: true
tested_devices:
- manufacturer: "MyUplink (NIBE, Bosch, Atlantic, Daikin, ...)"
Expand Down
27 changes: 26 additions & 1 deletion spec/manifest-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Each driver has a YAML manifest in `manifests/` that describes its metadata, cap
| `tier` | string | Yes | One of: `core`, `community`, `oem` |
| `author` | string | Yes (core) | Author name or organization |
| `protocol` | string | Yes | `modbus`, `mqtt`, `serial`, `standalone`, or `""` |
| `ders` | list | Yes | DER types: `pv`, `battery`, `meter`, `v2x_charger` |
| `ders` | list | Yes | DER types the driver covers (see below) |
| `control` | bool | Yes | Whether the driver supports EMS control commands |
| `tested_devices` | list | No | Devices tested against (see below) |
| `upstream_docs` | list | No | Vendor reference documents to watch for register/protocol changes (see below) |
Expand All @@ -25,6 +25,31 @@ Each driver has a YAML manifest in `manifests/` that describes its metadata, cap
| `bytecode_size` | int | No | Size of the `.luac` bytecode file in bytes |
| `changelog` | string | No | Version-specific release notes |

## DER Types

`ders` states which kinds of distributed energy resource a driver covers.

| Value | Device |
|-------|--------|
| `pv` | Solar PV |
| `battery` | Battery storage |
| `meter` | Electricity meter |
| `ev` | EV charger |
| `v2x_charger` | Bidirectional (V2X) charger |
| `heatpump` | Heat pump |
| `vehicle` | Vehicle |

It describes **the device, not the emit function used to report it.** A driver
that reports a heat pump only through `host.emit_metric`, without ever calling
`host.emit("heatpump", …)`, still declares `heatpump`: the field is how the
catalog files a driver, and an owner looking for their pump searches by the
device they own. Declaring the empty list files a driver under nothing, so it
cannot be found by device type at all — that is for a driver which genuinely
covers no DER, not for one whose telemetry is metrics-only.

`ders` reaches the signed artifact as its `capabilities`, so changing it changes
the published bytes and requires a version bump like any other source change.

## Tested Devices

The `tested_devices` list describes the device models a driver has been verified against.
Expand Down
2 changes: 1 addition & 1 deletion support-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@
},
{
"catalog_source": true,
"catalog_version": "1.1.0",
"catalog_version": "1.1.1",
"driver_id": "myuplink",
"package_id": null,
"targets": {
Expand Down