# Open Driver SDK — pointer

**Built 2026-05-13.** The open driver platform lives at
[`otonomo_drivers/`](../otonomo_drivers/) in this repo. It's intentionally
isolated from the deploy paths (`deploy_cloud.sh` only ships
`cloud/app/`; `deploy_pi.sh` only ships `pi_energy_control/`) so it
won't accidentally end up anywhere yet. Future move: extract to
`github.com/otonomo/drivers` once the brand and website are ready
(BOIP opposition window closes ~2026-07-04).

## What's in there

| Layer | Lives at | Notes |
|---|---|---|
| Driver SDK base classes | [`otonomo_drivers/otonomo_sdk/`](../otonomo_drivers/otonomo_sdk/) | `Driver`, `Metric`, `Command`, `CommandResult`, `Capability`, `DriverStatus`, schema validator, dynamic loader, wire codec, site manifest loader |
| MQTT publisher + cmd subscriber | [`otonomo_drivers/otonomo_sdk/mqtt/`](../otonomo_drivers/otonomo_sdk/mqtt/) | `MqttPublisher` (drives driver.poll() loop, emits wire-protocol v1 frames, retained site/manifest, LWT) + `MqttCommandSubscriber` (routes hems/<box>/cmd/<inst> to driver.execute(), publishes replies, dedup + deadline guards). Optional dep: `pip install otonomo-sdk[mqtt]`. |
| Canonical metric schemas | [`otonomo_drivers/otonomo_sdk/schemas/*.yaml`](../otonomo_drivers/otonomo_sdk/schemas/) | 10 device classes: pv_inverter, battery, grid_meter, heat_pump, gas_boiler, ev_charger, smart_plug, temperature_sensor, dehumidifier, smart_light |
| Reference drivers (home environment) | [`otonomo_drivers/drivers/`](../otonomo_drivers/drivers/) | 6 stable: solaredge_modbus, vaillant_ebus, daikin_local, shelly_rpc, qlima_local, wlbox2_local |
| Reference drivers (prospect environment, hems-900100) | [`otonomo_drivers/drivers/`](../otonomo_drivers/drivers/) | alphaess_modbus (battery+inverter), vaillant_eebus (HP, read-only), control4_director (lights), smappee_mqtt (submeter), homewizard_p1 (grid_meter — LIVE 2026-06-08) |
| Grid-meter reference drivers | [`otonomo_drivers/drivers/homewizard_p1/`](../otonomo_drivers/drivers/homewizard_p1/) + [`otonomo_drivers/drivers/youless_p1/`](../otonomo_drivers/drivers/youless_p1/) | Both `grid_meter` device class, `beta`. homewizard_p1 v0.2.0 (local REST, 1 s poll, validated on hems-900100); youless_p1 v0.1.0 (local REST, frequency caveats in README). |
| Wire protocol v1.0 spec | [`otonomo_drivers/docs/wire_protocol.md`](../otonomo_drivers/docs/wire_protocol.md) | MQTT topic structure + canonical JSON payload shapes + security model |
| Site manifest example | [`otonomo_drivers/examples/sites/home-brasschaat.yaml`](../otonomo_drivers/examples/sites/home-brasschaat.yaml) | 14 instances covering every device in the reference home |
| `otonomo` CLI | [`otonomo_drivers/otonomo_sdk/cli.py`](../otonomo_drivers/otonomo_sdk/cli.py) | `new-driver`, `validate`, `site validate`, `site capabilities` |
| Test suite | [`otonomo_drivers/tests/`](../otonomo_drivers/tests/) | 126 tests, all green; SDK + every driver + wire + site manifest + CLI |

## How drivers know what to publish, cloud knows what to expect

The seam is the **canonical metric schema** in
`otonomo_drivers/otonomo_sdk/schemas/`. Every PV inverter driver must
emit `pv.ac_power_w`, `pv.energy_today_wh`, `inverter.status` regardless
of brand. Vendor-specific quirks (SolarEdge meter offset 18 drift, eBUS
authorization levels, QLIMA compressor temp contamination, etc.) are
buried inside each driver's `poll()` implementation. Cloud control logic
reads only canonical names; brand names never appear above the driver
layer.

## How driver capabilities reach the Pro UI

The chain is documented in detail in
[`hems_capability_platform_plan_2026_05_13.md`](hems_capability_platform_plan_2026_05_13.md).
Short version:

```
site manifest YAML
    ↓ (otonomo_sdk.site.load_site_manifest)
parsed SiteManifest
    ↓ (otonomo_sdk.site.derive_capabilities)
set[str] of capability names
    ↓ (published to hems/<box>/site/manifest, ingested by cloud)
ops-db.box_capabilities
    ↓ (queried by cloud/app/blocks/capabilities.py)
visible Pro UI tabs for this box
```

## Status and publication

- **Public release:** ON HOLD until the website + reference content is
  ready (the user's call: "no public stuff just yet"). Brand assets,
  BOIP word-mark status, otonomo.be reference content all need to land
  first.
- **Internal use:** the SDK is the source of truth for driver behavior
  going forward. Phase C of the capability platform plan migrates the
  home Pi to this SDK.

## When the time comes to publish

The extraction is mechanical:

```bash
git filter-repo --subdirectory-filter otonomo_drivers/
# push to github.com/otonomo/drivers
# publish to PyPI: otonomo-sdk + otonomo-driver-<name> for each driver
```

Until then, treat it as a private library that happens to be Apache 2.0
licensed.

## Why this is in the main repo, not a separate one yet

Three reasons:

1. Active iteration on the contract — easier when drivers + cloud +
   spec are all in one place.
2. The "vendor-everything" rule applies once we ship to customers; until
   then, the SDK can be imported directly without PyPI plumbing.
3. The extraction is one command (`git filter-repo`) whenever we want.
