Otonomo home-energy intelligence

Open Driver SDK — pointer

Built 2026-05-13. The open driver platform lives at 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/ Driver, Metric, Command, CommandResult, Capability, DriverStatus, schema validator, dynamic loader, wire codec, site manifest loader
MQTT publisher + cmd subscriber otonomo_drivers/otonomo_sdk/mqtt/ MqttPublisher (drives driver.poll() loop, emits wire-protocol v1 frames, retained site/manifest, LWT) + MqttCommandSubscriber (routes hems//cmd/ to driver.execute(), publishes replies, dedup + deadline guards). Optional dep: pip install otonomo-sdk[mqtt].
Canonical metric schemas otonomo_drivers/otonomo_sdk/schemas/*.yaml 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/ 6 stable: solaredge_modbus, vaillant_ebus, daikin_local, shelly_rpc, qlima_local, wlbox2_local
Reference drivers (prospect environment, hems-900100) 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/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 MQTT topic structure + canonical JSON payload shapes + security model
Site manifest example otonomo_drivers/examples/sites/home-brasschaat.yaml 14 instances covering every device in the reference home
otonomo CLI otonomo_drivers/otonomo_sdk/cli.py new-driver, validate, site validate, site capabilities
Test suite 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.

Vendor-extension metrics (added 2026-07-19)

Not every useful reading is canonical. A SolarEdge StorCtl_Mod register or an AlphaESS per-pack fault bitfield has no cross-vendor meaning, so it must never become part of a device-class contract that every other vendor's driver would then be expected to satisfy.

Those live in a vendor namespace, declared in the driver's own manifest rather than in a canonical schema:

# drivers/solaredge_modbus/manifest.yaml
vendor_namespace: solaredge
vendor_metrics:
  solaredge.stor_ctl_mod:
    type: int
    description: 0=Disabled, 1=MaxSelfConsume, 2=TOU, 3=BackupOnly, 4=Remote.

Rules the validator enforces (SchemaValidator.validate_driver_poll):

Controllers must not read vendor metrics. They are for diagnostics, dashboards and support. Anything a cloud controller depends on has to be canonical, or the "no vendor-specific branches in controllers" non-negotiable is broken by the back door.

Vendor names are mapped to fleet-db metric_ids in cloud/app/blocks/telemetry_v1_consumer.py like any other metric — that mapping layer absorbs renames, so moving a metric into a vendor namespace needs no fleet-db migration.

How driver capabilities reach the Pro UI

The chain is documented in detail in 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

When the time comes to publish

The extraction is mechanical:

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.