openadr

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 9 Imported by: 0

README

go-openadr — OpenADR 3 for Go

Go Version Go Reference CI Status Interop Code Coverage Latest Release

A Go implementation of OpenADR 3, the demand-response protocol between utilities or aggregators and the energy resources of their customers. It follows the specification of the OpenADR Alliance, revision 3.1.0, whose documents are public: the OpenAPI definition, the Definition and the User Guide, in grid-coordination/openadr3-specification. It provides both sides of the REST API:

  • client: a VEN client and a Business Logic client for any OpenADR 3.1 VTN;
  • server: a VTN as an embeddable http.Handler;
  • openadr (the module root): the domain model both share;
  • notification/webhook, notification/mqtt and notification/websocket: the ways a VTN tells its clients, each for both sides;
  • timeline: the intervals of an event or a report as a table, with the timing rules of the specification applied, and as CSV;
  • discovery: finding a VTN on the local network, and being found.

The three roles

An OpenADR 3 system has three kinds of participant, and this module has a part for each:

Role What it does Here
VTN (virtual top node) The server in the middle. It stores programs, events and reports, authenticates the other two, decides who may see what, and notifies of changes. It does not make prices or decisions. server, an http.Handler to embed; examples/vtn runs one
Business Logic (BL) The utility's or aggregator's side: whatever application creates programs and publishes events, a tariff say, and reads the reports that come back. A client of the VTN, not a protocol of its own. client.BL; examples/tariff publishes a price schedule from a CSV file
VEN (virtual end node) The customer's side: an energy management system, a charger, a gateway. It reads the events meant for it, hands them to whatever controls the equipment, and reports back, on metered usage for example. Also a client of the VTN. client.VEN; examples/ven prints its schedule and reports
%%{init: {"flowchart": {"htmlLabels": false}}}%%
flowchart TB
    BL["`**Business Logic**
    utility or aggregator
    examples/tariff`"]
    VTN["`**VTN**
    OpenADR server
    examples/vtn`"]
    VEN["`**VEN**
    customer site
    examples/ven`"]
    EQ["`battery, charger
    or heat pump`"]

    BL -->|"programs, events"| VTN
    VTN -->|"reports"| BL
    VTN -->|"programs, events"| VEN
    VEN -->|"reports"| VTN
    VEN --- EQ

    style VTN stroke-width:3px

The arrows show which way the data goes, not who calls whom: every request is made by one of the two clients, to the VTN. The clients never talk to each other, and the VTN reaches a client by itself only to notify it of a change. Examples runs this loop as three programs, step by step.

Status: pre-release. OpenADR 3.1.0 only. All 45 operations of the specification are implemented in the VTN, with OAuth2 client credentials or JSON Web Tokens, scopes, object ownership, targeting, and notifications by webhook and by MQTT; a VTN can also serve clients that do not authenticate, compress its answers, and announce itself on the local network. Two things go beyond the specification, both shared with OpenLEADR and both off unless asked for: notifications over a WebSocket, and resource groups. The API may still change.

The clients are tested against the OpenLEADR VTN — MQTT and WebSocket notifications and resource groups included — and the VTN with the ElaadNL client; webhooks, and the VTN's side of MQTT and of WebSockets, are so far tested between this module's own two sides only. See INTEROP.md for the evidence, docs/CONFORMANCE.md for exactly what is covered and PLAN.md for what comes next.

Out of scope: OpenADR 2.x, dispatch or optimization logic, integrations with physical resources (Modbus, OPC UA, ...), a web UI and a CLI. The library implements the protocol; what to do with a price or a load-shed request is the application's business.

go-openadr vs OpenLEADR and the ElaadNL client

go-openadr is tested against two other open-source implementations of OpenADR 3 (see INTEROP.md): the OpenLEADR VTN and client library, and the ElaadNL openadr3-client. Both are good software, and each does things this module does not. The table shows where the three differ, so that you can pick the right one: where this module still has to catch up, and, in its lower half, what it has that the others do not show.

Capability otfabric/go-openadr OpenLEADR openleadr-rs ElaadNL openadr3-client
Language Go Rust Python
License MIT Apache-2.0 or MIT Apache-2.0
VTN (server) ✅ embeddable http.Handler ✅ standalone service —
VEN client ✅ ✅ ✅
Business Logic client ✅ ✅ same client ✅
OpenADR 3.1.0 ✅ ✅ ✅
OpenADR 3.0.1 — planned — ✅
Programs, events, reports, VENs, resources ✅ ✅ ✅
Subscriptions ✅ ✅ delivered over WebSocket ✅ with the operation names of 3.0.1 ⁸
Webhook notifications ✅ sender and receiver — — not documented
Webhook notifications that survive a restart of the VTN ✅ opt-in outbox ⁹ n/a n/a
Notifications of reads (operation READ) ✅ opt-in — not documented n/a
MQTT notifications ✅ publisher and subscriber ¹ ✅ ✅ client, optional extra
MQTT topic operations (12) ✅ ✅ 11, none for subscriptions ✅
WebSocket notifications (not in the specification) ✅ VTN and client ³ ✅ VTN, experimental —
OAuth2 client credentials, token caching and renewal ✅ ✅ ✅
Token endpoint discovery (/auth/server) ✅ ✅ ✅
Built-in token service for the VTN ✅ in memory, for tests and small setups ✅ users in the database n/a
JSON Web Tokens from an external identity provider ✅ static keys and JWKS ✅ n/a
VTN without authentication ✅ VTN option, and a client without credentials — not documented ✅ anonymous client
Targeting with target hiding ✅ ✅ n/a
Persistent storage ✅ journal file ²; SQLite and PostgreSQL stores as examples ⁷ ✅ PostgreSQL n/a
Storage behind an interface (bring your own) ✅ with a test suite for your store prepared, PostgreSQL only n/a
Resource groups (proposed for OpenADR 3.2) ✅ opt-in, as OpenLEADR has them ³ ✅ ✅
mDNS discovery of local VTNs ✅ announce and browse ⁴ ✅ —
Response compression ✅ gzip, deflate ⁶ ✅ gzip, br, deflate, zstd —
Typed, validated models ✅ ✅ ✅ pydantic
Extra validation by plugin — — ✅
Event intervals as tables ✅ timeline, with CSV ⁵ — ✅ pandas, optional
Cancellation and deadlines per call ✅ context.Context ✅ async — timeouts
Structured logging ✅ log/slog, opt-in, client and VTN ✅ ✅
Metrics hooks ✅ client, VTN and notifications; no metrics library needed — not documented —
Rate limiting on the VTN ✅ hook, token bucket included; 429 with Retry-After — not documented n/a
Client retries with backoff ✅ opt-in policy, Retry-After honoured — not documented — not documented
Error categories to test for ✅ sentinels for errors.Is ✅ error enum ✅ exceptions
Dependencies none (standard library) many crates; PostgreSQL to run several packages
Run against the OpenADR Alliance test suite — ✅ 166 of 168 applicable, by its authors — not documented
Cross-tested against other implementations in CI ✅ against both, clients and VTN ¹⁰ — not documented — not documented
MQTT tested on several brokers ✅ Mosquitto, NanoMQ, HiveMQ CE, EMQX — not documented — not documented
mDNS tested against other implementations ✅ Avahi, Apple mDNSResponder, OpenLEADR — not documented n/a
Failure-injection tests (store, network, webhooks, broker, tokens, crash) ✅ make chaos — not documented — not documented
Fuzzed parsers ✅ six targets — not documented — not documented
Authorization matrix: every operation as every kind of client ✅ 54 rows, five clients — not documented n/a
The three roles as runnable programs (VTN, Business Logic, VEN) ✅ examples, run together in the tests ✅ VTN and a client CLI —

¹ The MQTT client is this module's own (MQTT 3.1.1, QoS 1). The VTN's publisher and the subscriber are tested on four brokers: Eclipse Mosquitto, NanoMQ, HiveMQ Community Edition and EMQX. The subscriber is also tested with the notifications OpenLEADR publishes.

² One process, all objects in memory, every change appended to a file. Not a database: for a large VTN, put storage.Store on one.

³ Neither is part of OpenADR 3.1.0. Both follow OpenLEADR's representation, and this module's clients are tested against OpenLEADR for both. Resource groups will change with the specification that adopts them.

⁴ This module's own Multicast DNS, over IPv4. Tested in both directions with Avahi, with Apple's mDNSResponder and with OpenLEADR: each finds what this module announces, and this module finds what each announces.

⁵ Rows of Go values and CSV, not a data-frame library. It also applies the timing rules of the specification (defaults, "now", sub-intervals, events that repeat), which the table of the ElaadNL client leaves to its user.

✅ = available · — = not available or not documented · n/a = does not apply to a client library. For openleadr-rs v0.2.7 and openadr3-client 2.4.0, the versions go-openadr is tested against, from their public sources and documentation as of October 2026. If you spot an inaccuracy, please open an issue and we will correct it.

⁶ The two codings Go's standard library can write. br and zstd would each need a dependency, which this module does not take.

⁷ examples/storage implements the store on SQLite and on PostgreSQL. It is a module of its own, so that the database drivers are not dependencies of go-openadr, and it is an example: read it, copy it, hold your own to the same test suite (storagetest.Run).

⁸ openadr3-client 2.4.0 names the operations of a subscription GET, POST, PUT and DELETE, as OpenADR 3.0.1 did; 3.1.0 names them READ, CREATE, UPDATE and DELETE. Against a 3.1.0 VTN, this one or OpenLEADR's, it can make and read a subscription to DELETE only. See INTEROP.md.

⁹ server.Config.Outbox: a notification is recorded before it is sent and forgotten when the webhook has confirmed it, so that what was on its way when the VTN stopped is sent after it starts again. The journal file store is an outbox, and so are the SQLite and PostgreSQL examples.

¹⁰ This module's clients against the OpenLEADR VTN, and the ElaadNL client against this module's VTN: objects, subscriptions with a webhook delivered, MQTT, resource groups, access without a token, compression, TLS.

Choose go-openadr if you want both sides of the protocol in one Go module without dependencies, a VTN you embed in your own service with your own identity provider and store, webhook notifications, or the hooks a service in production wants: metrics, logs, retries with backoff and errors to switch on.

Choose OpenLEADR if you want a VTN to deploy as it is, backed by PostgreSQL, that its authors have run against the Alliance's test suite.

Choose the ElaadNL client if you write a VEN or Business Logic in Python, or need OpenADR 3.0.1.

Install

go get github.com/otfabric/go-openadr

Go 1.23 or later. No dependencies outside the standard library.

A VEN

ven, err := client.NewVEN(client.Config{
    BaseURL:     "https://vtn.example.com/openadr3",
    Credentials: &client.Credentials{ClientID: id, ClientSecret: secret},
})
if err != nil {
    return err
}

events, err := ven.Events(ctx, client.EventQuery{ProgramID: programID, ActiveOnly: true})
if err != nil {
    return err
}
for _, event := range events {
    for _, interval := range event.Intervals {
        for _, payload := range interval.Payloads {
            if price, ok := payload.Values[0].Number(); ok && payload.Type == "PRICE" {
                fmt.Println(interval.ID, price)
            }
        }
    }
}

_, err = ven.CreateReport(ctx, openadr.Report{
    EventID:    events[0].ID,
    ClientName: "site-42",
    Resources: []openadr.ReportResource{{
        ResourceName: "main-meter",
        Intervals: []openadr.Interval{{ID: 0, Payloads: []openadr.ValuesMap{
            {Type: "USAGE", Values: []openadr.Value{openadr.Number(1.8)}},
        }}},
    }},
})

The client discovers the token endpoint from the VTN (GET /auth/server), obtains a token, keeps it until shortly before it expires, and fetches a new one when the VTN answers 401. client.BL is the same for the Business Logic side: it creates programs and events, provisions VENs and their targets, and reads reports.

Errors from the VTN are *openadr.Error values and match sentinels:

if errors.Is(err, openadr.ErrNotFound) { ... }

A VTN

vtn, err := server.New(server.Config{
    Store:     storage.NewMemory(),
    Validator: validator,              // resolves a bearer token to a client ID and scopes
    TokenURL:  "https://idp.example.com/oauth/token",
})
if err != nil {
    return err
}
http.Handle("/openadr3/", http.StripPrefix("/openadr3", vtn))

The server does not bundle an identity provider: it takes an auth.Validator. For tests and small deployments, auth.Issuer is an in-memory client-credentials token service that is both the /auth/token handler and the validator, and auth.JWTValidator validates the JSON Web Tokens of an identity provider, with static keys or its key set (JWKS). storage.OpenFile is a store that survives a restart. TokenURL is what GET /auth/server tells clients and has to be an absolute URL.

examples/quickstart runs a VTN, a Business Logic client and a VEN through the program → event → report workflow in one process:

go run ./examples/quickstart

examples/vtn is a standalone VTN to point other clients at.

Public information

A VTN whose programs are for everyone, a price list say, can let clients in without a token, as the specification provides:

vtn, err := server.New(server.Config{ /* ... */ AllowAnonymous: true})

Such a client reads the programs and events that have no targets, and nothing else; a request that carries a token is checked as always. A client is anonymous by having no credentials: client.NewVEN(client.Config{BaseURL: url}). examples/prices runs both.

Finding a VTN on the local network

A VTN at a customer site, a gateway, announces itself with DNS-SD over Multicast DNS, and a VEN on the same network finds it without being configured:

ad, err := discovery.Advertise(discovery.Advertisement{Port: 8443, BasePath: "openadr3"})
defer ad.Close()
vtns, err := discovery.Browse(ctx, discovery.Options{First: true})
if err == nil && len(vtns) > 0 {
    ven, err = client.NewVEN(client.Config{BaseURL: vtns[0].URL(), Credentials: credentials})
}

What is found is an address to try, not a VTN to trust: see SECURITY.md.

Intervals as a table

An event writes its intervals compactly: a default start and duration, an interval that overrides one of them or follows the one before, a start that means "now", several values packed into one interval, a duration of the event that cuts the intervals short or repeats them. timeline applies those rules and returns rows with a start and an end:

rows, err := timeline.OfEvent(event, timeline.Options{SubIntervals: true})
if err != nil {
    return err
}
for _, row := range rows.At(time.Now()) {      // what applies now
    fmt.Println(row.Type, row.Values, "until", row.End)
}
err = rows.WriteCSV(os.Stdout)

The other way, a table read from CSV becomes the intervals of an event:

table, err := timeline.ReadCSV(file)   // columns: start, end, type, values
event := openadr.Event{ProgramID: programID, Intervals: table.Intervals()}

Notifications

A client that would rather be told than poll subscribes with a webhook:

http.Handle("/openadr", &webhook.Handler{
    BearerToken: hookToken, // made up for this subscription
    Notify: func(ctx context.Context, n openadr.Notification) error {
        if event, ok := n.Object.(openadr.Event); ok {
            // n.Operation is CREATE, UPDATE or DELETE
        }
        return nil
    },
})

_, err = ven.CreateSubscription(ctx, openadr.Subscription{
    ClientName: "site-42",
    ObjectOperations: []openadr.ObjectOperation{{
        Objects:     []openadr.ObjectType{openadr.ObjectEvent},
        Operations:  []openadr.Operation{openadr.OperationCreate, openadr.OperationUpdate},
        CallbackURL: "https://site-42.example.com/openadr",
        BearerToken: hookToken,
    }},
})

The VTN verifies the callback URL before it accepts the subscription, so the handler has to be reachable first. A VTN serves subscriptions when it is given a sender:

sender := webhook.NewSender(webhook.SenderConfig{})
defer sender.Close(ctx)
vtn, err := server.New(server.Config{ /* ... */ Webhooks: sender})

Delivery is at least once. By default it is not durable: what is on its way when the VTN stops is lost. A VTN with an outbox records each notification before it sends it, and sends again after a restart what no webhook confirmed, in the order it happened, for as long as NotificationTTL says:

store, err := storage.OpenFile("vtn.journal", storage.FileOptions{}) // a Store and an Outbox
vtn, err := server.New(server.Config{ /* ... */ Store: store, Webhooks: sender, Outbox: store})
defer vtn.Close()

A client that must not miss a change still polls now and then: an outbox does not help a webhook that is away for longer than that. examples/notifications runs both sides.

A subscription may name the operation READ. OpenADR 3.1.0 lists it and does not say what it means; a VTN with server.Config.NotifyReads notifies such a subscriber whenever an object it may see is read, by ID or in a search. It is off by default.

A VTN with an MQTT broker publishes the same notifications there (server.Config.MQTT with a mqtt.Publisher), and a client asks the VTN for the broker and for its topics and subscribes:

notifiers, err := ven.Notifiers(ctx)                              // where the broker is
topics, err := ven.MQTTTopics(ctx, client.EventsForVEN(venID))    // what this VEN may hear
sub, err := mqtt.NewSubscriber(mqtt.SubscriberConfig{
    Broker: mqtt.Broker{URL: notifiers.MQTT.URIs[0]},
    Topics: []string{topics.All},
    Notify: func(ctx context.Context, topic string, n openadr.Notification) { /* ... */ },
})

The broker is yours to run, and its access control decides who can read which topic: see SECURITY.md.

A third way needs neither an address the VTN can reach nor a broker: the client opens a WebSocket and is told there. This is not part of OpenADR 3.1.0; it is an extension of OpenLEADR, which this module's VTN offers when it is given server.Config.WebSocket, and GET /notifiers says whether a VTN has it.

_, err = ven.CreateSubscription(ctx, openadr.Subscription{
    ClientName: "site-42",
    ObjectOperations: []openadr.ObjectOperation{{
        Objects:    []openadr.ObjectType{openadr.ObjectEvent},
        Operations: []openadr.Operation{openadr.OperationCreate, openadr.OperationUpdate},
        Mechanism:  openadr.MechanismWebSocket,      // in place of a callback URL
    }},
})
sub, err := websocket.NewSubscriber(websocket.SubscriberConfig{
    Dial:   ven.NotificationStream,
    Notify: func(ctx context.Context, n openadr.Notification) { /* ... */ },
})

The subscriber reconnects by itself. Whichever way it is notified, a client that must not miss a change searches after every reconnect: MQTT and the WebSocket keep nothing for a client that is away, and a webhook only what an outbox holds. examples/websocket runs both sides.

Examples

Example What it shows
quickstart The basic workflow in one process: program, event, report.
notifications Subscriptions and webhooks, both sides.
websocket Notifications over a WebSocket, both sides.
prices A VTN with a public tariff, a client without credentials, the prices as a table.
resourcegroups Resource groups, as Business Logic and as a VEN see them.
discovery Announcing a VTN on the local network, and finding it.
vtn A standalone VTN with flags for every option, to point clients at.
ven A VEN for the command line: finds a VTN, registers, prints its schedule, reports, watches.
tariff Business Logic for the command line: publishes a schedule from a CSV file, and reads the reports on it.
observability Metrics and logs of a client and a VTN, a retry policy at work against a gateway that fails, and failures told apart by errors.Is.
storage The VTN on SQLite and on PostgreSQL: the store interface implemented on a database. A module of its own.

The first six and observability are self-contained and run as they are (go run ./examples/quickstart); CI runs them. vtn, ven and tariff work together:

# the VTN, with a client for each of the other two
go run ./examples/vtn -client utility:utility-secret:bl -client site-42:site-42-secret:ven -websocket &

# Business Logic publishes a tariff
BL_CLIENT_SECRET=utility-secret go run ./examples/tariff -url http://127.0.0.1:8080 -client-id utility -program day-ahead -csv prices.csv

# the VEN reads it and reports its usage (with -watch it stays and is told of changes)
VEN_CLIENT_SECRET=site-42-secret go run ./examples/ven -url http://127.0.0.1:8080 -client-id site-42 -report

# Business Logic reads the report
BL_CLIENT_SECRET=utility-secret go run ./examples/tariff -url http://127.0.0.1:8080 -client-id utility -program day-ahead -reports

What goes over the wire while they run:

sequenceDiagram
    autonumber
    participant BL as Business Logic (examples/tariff)
    participant VTN as VTN (examples/vtn)
    participant VEN as VEN (examples/ven)

    BL->>VTN: POST /programs
    BL->>VTN: POST /events, the 24 prices
    VEN->>VTN: POST /vens, registers itself
    VEN->>VTN: GET /events
    VTN-->>VEN: the events it may see
    Note over VEN: lays the prices out as a table,<br/>acts on them
    VEN->>VTN: POST /reports, usage per interval
    BL->>VTN: GET /reports
    VTN-->>BL: what the VENs reported
    Note over VTN,VEN: with -watch, the VEN subscribes and the VTN<br/>tells it of every later change to an event

Each first asks the VTN for a token (GET /auth/server, then the token endpoint), which the diagram leaves out. TestThreeRolesAsPrograms in e2e/ runs exactly these four commands on every run of the tests, with a restart of the VTN in the middle.

prices.csv has a header line and one line per period:

start,end,type,values
2026-10-14T00:00:00Z,2026-10-14T01:00:00Z,PRICE,0.11
2026-10-14T01:00:00Z,2026-10-14T02:00:00Z,PRICE,0.12

Things to know

  • Concurrency. Clients and the server are safe for concurrent use.
  • Timeouts. Every client call takes a context that bounds the whole call, including a token request it may trigger. The default HTTP client has a 30 second timeout; pass your own http.Client for TLS settings, proxies or other limits.
  • Paging. A VTN returns at most 50 objects per search. client.Each iterates over a whole result.
  • Values. JSON does not distinguish integers from other numbers, so payload numbers are float64.
  • Validation. Objects are checked against the schema before they are sent; a violation is an *openadr.ValidationError and no request is made. Payload types, units and other enumerations of the specification are plain strings and are not restricted, so private values work.
  • Compression. The VTN compresses answers of 1 KiB and more with gzip or deflate for a client that accepts one, which the clients of this module do (gzip).
  • Extensions. Resource groups and WebSocket notifications are not part of OpenADR 3.1.0. A VTN has neither unless its configuration says so, and the clients' methods for them are answered with 404 or 501 by a VTN that has not.
  • Shutdown. A VTN with WebSockets has connections of its own: call Server.Close when it stops, before the HTTP server's Shutdown.
  • Errors. Every failure matches a sentinel of package openadr with errors.Is: ErrNotFound, ErrConflict, ErrUnavailable, ErrTimeout, ErrInvalid and the rest of ERRORS.md.
  • Retries. None by default, beyond one retry with a fresh token after a 401. client.Config.RetryPolicy adds them with backoff, for what is safe to repeat: see OBSERVABILITY.md.
  • Rate limiting. None by default. server.Config.Limiter is asked for every request of an identified client; server.NewRateLimiter is a token bucket per client. A refused request is answered 429 with Retry-After, which a client of this module with a retry policy waits out.
  • Logging and metrics. The library is silent and counts nothing unless told to: each part takes a slog.Logger and a small metrics interface in its configuration. See OBSERVABILITY.md.
  • Storage. The VTN keeps its objects behind storage.Store: in memory, in a journal file, or on a database of yours (examples/storage has SQLite and PostgreSQL).

Specification

Documentation

  • API.md: the public API by package, and the behaviour that signatures do not show.
  • ERRORS.md: every error a caller can get, and how to tell them apart.
  • OBSERVABILITY.md: logging, metrics and retries.
  • INTEROP.md: what has been tested against which other implementation, and how to repeat it.
  • docs/CONFORMANCE.md: operation coverage, and how the VTN reads the parts of the specification that leave room.
  • ARCHITECTURE.md: packages, the version strategy, and the storage and authentication boundaries.
  • SECURITY.md: reporting a vulnerability, and what to know before deploying.
  • RELEASE.md: release notes. PLAN.md: roadmap and status. CONTRIBUTING.md: how to change things.

The reference implementations it is tested against, and a conformance suite that asserts the specification, come from a separate repository, otfabric/openadr-interop, which shares no code with this one.

Development

make check    # format, lint, vet, vulnerability scan, tests with -race, coverage, examples
make fuzz     # the parser fuzz targets
make e2e      # this module's clients against this module's VTN, every feature
make chaos    # many clients at once, and failures injected: store, network, webhooks, broker, tokens, crashes
make storage  # the SQLite and PostgreSQL example stores, a module of their own (PostgreSQL needs Docker)
make interop  # against the OpenLEADR and ElaadNL reference images (needs Docker, see INTEROP.md)

The tests of e2e are part of make check. Those that inject failures are random and follow a seed, which a failing test prints; see CONTRIBUTING.md.

License

MIT, see LICENSE.

Documentation

Overview

Package openadr defines the domain model of OpenADR 3: programs, events, reports, VENs, resources and the value types they are built from.

The types in this package are independent of any one protocol revision's JSON representation. Encoding and decoding for a specific revision (3.1.0 today) is an internal concern of the client and server packages, so the structures here carry no JSON tags and are not meant to be marshalled directly.

Entry points:

  • package client: VEN and Business Logic clients for a VTN.
  • package server: an embeddable VTN http.Handler.
  • package auth: bearer-token validation and OAuth2 scopes.
  • packages notification/webhook, notification/mqtt and notification/websocket: the ways a VTN notifies its clients.
  • package timeline: the intervals of an event or report as a table.
  • package discovery: finding a VTN on the local network.

ResourceGroup, MechanismWebSocket and what goes with them are not part of OpenADR 3.1.0: they are extensions shared with OpenLEADR, which a VTN has only when it is configured to.

OpenADR 2.x is out of scope, as are dispatch algorithms and integrations with physical energy resources.

Index

Constants

View Source
const (
	MQTTAnonymous   = "ANONYMOUS"
	MQTTBearerToken = "OAUTH2_BEARER_TOKEN"
	MQTTCertificate = "CERTIFICATE"
)

MQTT authentication methods defined by OpenADR 3.1.0.

View Source
const AggregatedReport = "AGGREGATED_REPORT"

AggregatedReport is the resource name of an aggregated report.

Variables

View Source
var (
	ErrBadRequest   = errors.New("openadr: bad request")
	ErrUnauthorized = errors.New("openadr: unauthorized")
	ErrForbidden    = errors.New("openadr: forbidden")
	ErrNotFound     = errors.New("openadr: not found")
	ErrConflict     = errors.New("openadr: conflict")

	// ErrTooManyRequests is a 429: the VTN, or a gateway before it, wants
	// the client to slow down. Error.RetryAfter says for how long, when
	// the answer did.
	ErrTooManyRequests = errors.New("openadr: too many requests")
	// ErrServer is any 5xx: the VTN failed, and the request may or may
	// not have been carried out. ErrNotImplemented and ErrUnavailable are
	// two of them, and an error that matches one of those matches this too.
	ErrServer = errors.New("openadr: server error")
	// ErrNotImplemented is a 501: the VTN does not have the operation,
	// such as subscriptions on a VTN that cannot notify.
	ErrNotImplemented = errors.New("openadr: not implemented")
	// ErrUnavailable is a 502, 503 or 504: the VTN cannot be reached
	// through its gateway, or cannot answer now. The request was not
	// carried out when the status is 503.
	ErrUnavailable = errors.New("openadr: unavailable")
)

Categories of failure reported by a VTN. An *Error matches the one for its HTTP status through errors.Is. The storage layer of the server returns ErrNotFound and ErrConflict directly.

Test for these with errors.Is, and for the types with errors.As; the text of an error is for people and may change.

View Source
var (
	// ErrInvalid is matched by every *ValidationError: an object, an ID or
	// a message does not satisfy the schema.
	ErrInvalid = errors.New("openadr: invalid")
	// ErrConfiguration is matched by what a constructor of this module
	// returns for a configuration it cannot work with.
	ErrConfiguration = errors.New("openadr: invalid configuration")
	// ErrTimeout is matched by a call that ran out of time: the deadline
	// of its context, the timeout of the HTTP client, or a network
	// timeout below them. Such an error still matches
	// context.DeadlineExceeded where that is what happened.
	ErrTimeout = errors.New("openadr: timeout")
	// ErrProtocol is matched by an answer that is not what the protocol
	// says it is: a body that cannot be decoded, one that is too large, a
	// token endpoint that issues no token. The peer acted on the request.
	ErrProtocol = errors.New("openadr: protocol error")
	// ErrClosed is matched by the use of something after it was closed.
	ErrClosed = errors.New("openadr: closed")
)

Categories of failure that are not an answer of a VTN.

View Source
var Infinite = Duration{Years: 9999}

Infinite is P9999Y, which the specification uses to mean "indefinitely".

Functions

func ValidID

func ValidID(id string) bool

ValidID reports whether id is a well-formed object ID: 1 to 128 characters of letters, digits, '_' and '-'.

Types

type Duration

type Duration struct {
	Negative bool
	Years    int64
	Months   int64
	Weeks    int64
	Days     int64
	Hours    int64
	Minutes  int64
	Seconds  float64
}

Duration is an ISO 8601 duration as OpenADR uses it, for example PT1H or P1DT12H. Years and months are kept as written because their length depends on the date they are applied to; use AddTo for calendar arithmetic and TimeDuration when the duration has no such components.

The format allows either days or weeks, not both; String writes weeks only when Days is zero.

func DurationOf

func DurationOf(td time.Duration) Duration

DurationOf converts a time.Duration, using hours, minutes and seconds only.

func ParseDuration

func ParseDuration(s string) (Duration, error)

ParseDuration parses an ISO 8601 duration in the form the OpenADR 3 schema accepts.

func (Duration) AddTo

func (d Duration) AddTo(t time.Time) time.Time

AddTo returns t moved by the duration, applying years, months and days on the calendar in t's location.

func (Duration) IsInfinite

func (d Duration) IsInfinite() bool

IsInfinite reports whether the duration is P9999Y.

func (Duration) IsZero

func (d Duration) IsZero() bool

IsZero reports whether the duration has no length.

func (Duration) String

func (d Duration) String() string

String returns the ISO 8601 form. The zero Duration is "PT0S".

func (Duration) TimeDuration

func (d Duration) TimeDuration() (time.Duration, bool)

TimeDuration converts to a time.Duration, taking a day as 24 hours. It reports false when the duration has years or months, whose length is not fixed, or does not fit in a time.Duration.

type Error

type Error struct {
	// Method and Path identify the request.
	Method string
	Path   string
	// StatusCode is the HTTP status of the response.
	StatusCode int
	Problem    Problem
	// RetryAfter is how long the VTN asked the client to wait before it
	// tries again, in a Retry-After header; zero when it did not say.
	RetryAfter time.Duration
}

Error is a non-success response from a VTN. Problem is zero when the response body was not a problem object.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is matches the category sentinel for the response's status code.

type Event

type Event struct {
	Metadata

	// ProgramID is the program the event belongs to.
	ProgramID string
	Name      string
	// Duration optionally bounds the event; it may be used to loop intervals.
	Duration *Duration
	// Priority orders events; a lower number is a higher priority.
	Priority           *int
	Targets            []string
	ReportDescriptors  []ReportDescriptor
	PayloadDescriptors []EventPayloadDescriptor
	// IntervalPeriod sets the default start and duration of Intervals.
	IntervalPeriod *IntervalPeriod
	Intervals      []Interval
}

Event communicates a demand-response request to VENs.

func (Event) Validate

func (e Event) Validate() error

Validate checks the client-provided properties against the schema. It does not look at Metadata.

type EventPayloadDescriptor

type EventPayloadDescriptor struct {
	PayloadType string
	Units       string
	Currency    string
}

EventPayloadDescriptor gives context, such as units and currency, to event payloads of one type.

type Interval

type Interval struct {
	ID             int32
	IntervalPeriod *IntervalPeriod
	Payloads       []ValuesMap
}

Interval is a temporal window with payloads. ID is chosen by the client that creates the interval and is not a sequence number.

type IntervalPeriod

type IntervalPeriod struct {
	Start    *time.Time
	Duration *Duration
	// RandomizeStart is the range of a client-applied offset to Start.
	RandomizeStart *Duration
}

IntervalPeriod defines the temporal aspects of one or more intervals.

The specification gives two values a special meaning: a Start in year 1 may mean "now", and a Duration of P9999Y may mean "indefinitely"; see Duration.IsInfinite.

type MQTTAuthentication

type MQTTAuthentication struct {
	// Method is MQTTAnonymous, MQTTBearerToken or MQTTCertificate.
	Method string
	// Username goes with MQTTBearerToken: the user name to present, with
	// the client's OAuth2 token as the password. The value "{clientID}"
	// stands for the client's own ID.
	Username string
	// CACert, ClientCert and ClientKey go with MQTTCertificate, in PEM.
	CACert     string
	ClientCert string
	ClientKey  string
}

MQTTAuthentication says how a client authenticates with the broker.

type MQTTBinding

type MQTTBinding struct {
	// URIs are the addresses of the broker, such as mqtts://broker.example.
	URIs []string
	// Serialization is the format of the messages; always "JSON" so far.
	Serialization  string
	Authentication MQTTAuthentication
}

MQTTBinding is what a client needs to receive notifications by MQTT.

type Mechanism added in v0.2.0

type Mechanism string

Mechanism is a way for a VTN to deliver the notifications of a subscription.

const (
	// MechanismWebhook delivers to the callback URL. It is what OpenADR
	// 3.1.0 defines, and what the empty Mechanism means.
	MechanismWebhook Mechanism = "WEBHOOK"
	// MechanismWebSocket delivers over the WebSocket the subscriber has
	// open with the VTN, and needs no callback URL. It is not part of
	// OpenADR 3.1.0: it is an extension of OpenLEADR that this module
	// shares. A VTN says in GET /notifiers whether it has it.
	MechanismWebSocket Mechanism = "WEBSOCKET"
)

type Metadata

type Metadata struct {
	ID       string
	Created  time.Time
	Modified time.Time
}

Metadata is assigned by the VTN when an object is created or modified. A VTN ignores these fields in create and update requests, so callers leave them zero when building an object to send.

type Notification

type Notification struct {
	// ID identifies the operation the notification is about, for a VTN
	// that sends one: OpenADR 3.1.0 has no such property, OpenLEADR does.
	// A client that is notified over several channels can use it to tell
	// that two notifications are the same.
	ID         string
	ObjectType ObjectType
	Operation  Operation
	// Object is the object the operation happened to, as the subscriber
	// may see it: a Program, Event, Report, Subscription, VEN, Resource or
	// ResourceGroup value, according to ObjectType.
	Object any
	// Targets are the targets of the object that the subscriber may see.
	Targets []string
}

Notification is what a VTN sends to a webhook, publishes by MQTT or writes to a WebSocket.

type Notifiers

type Notifiers struct {
	// Webhook says the VTN serves subscriptions with callback URLs.
	Webhook bool
	// WebSocket says the VTN serves subscriptions with MechanismWebSocket,
	// which is not part of OpenADR 3.1.0.
	WebSocket bool
	// MQTT describes the VTN's MQTT broker; nil when it has none.
	MQTT *MQTTBinding
}

Notifiers says how a VTN notifies its clients: the answer to GET /notifiers.

type ObjectOperation

type ObjectOperation struct {
	Objects    []ObjectType
	Operations []Operation
	// Mechanism is how to notify; empty for a webhook.
	Mechanism Mechanism
	// CallbackURL is the webhook. It has to use https, and the VTN checks
	// that it answers before it accepts the subscription. It is required
	// for a webhook and has no use otherwise.
	CallbackURL string
	// BearerToken, when set, is sent by the VTN with every notification so
	// that the webhook can tell the VTN from anyone else.
	BearerToken string
}

ObjectOperation selects operations on kinds of objects and says how the subscriber is to be told of them: by default, at a webhook.

type ObjectType

type ObjectType string

ObjectType names a kind of object addressable through the API.

const (
	ObjectProgram      ObjectType = "PROGRAM"
	ObjectEvent        ObjectType = "EVENT"
	ObjectReport       ObjectType = "REPORT"
	ObjectSubscription ObjectType = "SUBSCRIPTION"
	ObjectVEN          ObjectType = "VEN"
	ObjectResource     ObjectType = "RESOURCE"
)

Object types defined by OpenADR 3.1.0.

const ObjectResourceGroup ObjectType = "RESOURCE_GROUP"

ObjectResourceGroup is the object type of a ResourceGroup, which is not part of OpenADR 3.1.0.

type Operation

type Operation string

Operation is something that happens to an object and that a client can subscribe to.

const (
	OperationCreate Operation = "CREATE"
	OperationRead   Operation = "READ"
	OperationUpdate Operation = "UPDATE"
	OperationDelete Operation = "DELETE"
)

Operations defined by OpenADR 3.1.0. A VTN notifies of CREATE, UPDATE and DELETE; READ is in the schema and is never notified of by this module.

type PayloadDescriptor

type PayloadDescriptor interface {
	// contains filtered or unexported methods
}

PayloadDescriptor is an EventPayloadDescriptor or a ReportPayloadDescriptor.

type Point

type Point struct {
	X, Y float64
}

Point is a pair of numbers, typically a point on a two dimensional grid.

type Problem

type Problem struct {
	Type     string
	Title    string
	Status   int
	Detail   string
	Instance string
}

Problem is the error body a VTN returns with a 4xx or 5xx response.

type Program

type Program struct {
	Metadata

	// Name identifies the program and is unique within a VTN.
	Name string
	// IntervalPeriod is the temporal span of the program.
	IntervalPeriod *IntervalPeriod
	// Descriptions link to human or machine readable descriptions.
	Descriptions []ProgramDescription
	// PayloadDescriptors give context to the payloads of the program's
	// events and reports. Each element is an EventPayloadDescriptor or a
	// ReportPayloadDescriptor.
	PayloadDescriptors []PayloadDescriptor
	Attributes         []ValuesMap
	// Targets restrict which VENs may read the program. A VTN only shows a
	// VEN the targets that VEN asked for.
	Targets []string
}

Program is a demand-response program or tariff.

func (Program) Validate

func (p Program) Validate() error

Validate checks the client-provided properties against the schema. It does not look at Metadata.

type ProgramDescription

type ProgramDescription struct {
	URL string
}

ProgramDescription links to a description of a program.

type Report

type Report struct {
	Metadata

	// ClientID is written by the VTN from the creating client's token.
	ClientID string
	// EventID is the event the report is associated with.
	EventID string
	// ClientName is chosen by the reporting client.
	ClientName         string
	Name               string
	PayloadDescriptors []ReportPayloadDescriptor
	Resources          []ReportResource
}

Report carries data from a VEN in response to an event.

func (Report) Validate

func (r Report) Validate() error

Validate checks the client-provided properties against the schema. It does not look at Metadata or ClientID, which the VTN writes.

type ReportDescriptor

type ReportDescriptor struct {
	PayloadType string
	ReadingType string
	Units       string
	Targets     []string
	// Aggregate asks for one result over all targeted resources.
	Aggregate bool
	// StartInterval is the interval on which to report; -1 is the end of
	// the last interval.
	StartInterval int32
	// NumIntervals is the number of intervals to include; -1 is all.
	NumIntervals int32
	// Historical reports on the intervals before StartInterval, as opposed
	// to those after it.
	Historical bool
	// Frequency is the number of intervals between reports; -1 is the same
	// as NumIntervals.
	Frequency int32
	// Repeat is the number of reports to generate; -1 is indefinitely.
	Repeat          int32
	ReportIntervals ReportIntervals
}

ReportDescriptor requests a report from a VEN. NewReportDescriptor returns one holding the defaults of the specification; the zero value does not.

func NewReportDescriptor

func NewReportDescriptor(payloadType string) ReportDescriptor

NewReportDescriptor returns a descriptor for payloadType with every other field at the default the specification defines for it.

type ReportIntervals

type ReportIntervals string

ReportIntervals says which intervals a requested report covers.

const (
	ReportIntervalsIntervals    ReportIntervals = "INTERVALS"
	ReportIntervalsSubIntervals ReportIntervals = "SUB_INTERVALS"
	ReportIntervalsOpen         ReportIntervals = "OPEN_INTERVALS"
)

Report interval options defined by OpenADR 3.1.0.

type ReportPayloadDescriptor

type ReportPayloadDescriptor struct {
	PayloadType string
	ReadingType string
	Units       string
	Accuracy    *float64
	// Confidence is a percentage, 0 to 100.
	Confidence *int
}

ReportPayloadDescriptor gives context, such as units and data quality, to report payloads of one type.

type ReportResource

type ReportResource struct {
	ResourceName   string
	IntervalPeriod *IntervalPeriod
	Intervals      []Interval
}

ReportResource is the report data of one resource. A ResourceName of AggregatedReport denotes data aggregated over several resources.

type Resource

type Resource struct {
	Metadata

	ClientID string
	// VENID is the VEN the resource belongs to.
	VENID string
	// Name is unique within its VEN.
	Name       string
	Attributes []ValuesMap
	// Targets are granted by Business Logic; see VEN.Targets.
	Targets []string
}

Resource is a resource of a VEN, such as a device or a group of devices.

func (Resource) Validate

func (r Resource) Validate() error

Validate checks the client-provided properties against the schema. ClientID is not required here: whether it is depends on who sends the object.

type ResourceGroup added in v0.2.0

type ResourceGroup struct {
	Metadata

	Name       string
	Attributes []ValuesMap
	Targets    []string
	// Children are the members of the group. A VEN is shown the resources
	// that are its own, and the groups that lead to one.
	Children []ResourceGroupChild
}

ResourceGroup is a named set of resources and of other resource groups, by which Business Logic arranges the resources of its VENs, for example by feeder or by building.

Resource groups are not part of OpenADR 3.1.0. They are proposed for a later revision and implemented here as OpenLEADR implements them; expect them to change with the specification.

func (ResourceGroup) Validate added in v0.2.0

func (g ResourceGroup) Validate() error

Validate checks the client-provided properties.

type ResourceGroupChild added in v0.2.0

type ResourceGroupChild struct {
	Type ResourceGroupChildType
	ID   string
}

ResourceGroupChild is a member of a resource group: the ID of a resource or of a resource group.

type ResourceGroupChildType added in v0.2.0

type ResourceGroupChildType string

ResourceGroupChildType says what a member of a resource group is.

const (
	// ChildResource is a resource of a VEN.
	ChildResource ResourceGroupChildType = "ven_resource"
	// ChildResourceGroup is another resource group.
	ChildResourceGroup ResourceGroupChildType = "resource_group"
)

The kinds of member a resource group has.

type Subscription

type Subscription struct {
	Metadata

	// ClientID is written by the VTN from the creating client's token.
	ClientID string
	// ClientName is chosen by the subscribing client.
	ClientName string
	// ProgramID, when set, limits the subscription to that program, its
	// events and the reports on them.
	ProgramID string
	// ObjectOperations say what to notify of, and where.
	ObjectOperations []ObjectOperation
	// Targets are the targets the subscriber names, as in a search: see
	// the access rules for programs and events.
	Targets []string
}

Subscription asks a VTN to notify a client when objects are created, updated or deleted.

func (Subscription) Validate

func (s Subscription) Validate() error

Validate checks the client-provided properties against the schema. It does not look at Metadata or ClientID, which the VTN writes.

type Topics

type Topics struct {
	Create string
	Update string
	Delete string
	// All is a topic filter that matches the other three.
	All string
}

Topics are the MQTT topics on which a VTN publishes the operations on a set of objects. A field is empty where the VTN has no such topic: there is no Create for a single object, and All is optional.

type VEN

type VEN struct {
	Metadata

	// ClientID ties the VEN to the client credentials it authenticates with.
	// Business Logic supplies it; for a VEN-created object the VTN writes it
	// from the token.
	ClientID string
	// Name is unique within a VTN.
	Name       string
	Attributes []ValuesMap
	// Targets are granted by Business Logic and decide which targeted
	// programs and events the VEN may read. Only Business Logic may set them.
	Targets []string
}

VEN is a virtual end node known to the VTN.

func (VEN) Validate

func (v VEN) Validate() error

Validate checks the client-provided properties against the schema. ClientID is not required here: whether it is depends on who sends the object.

type ValidationError

type ValidationError struct {
	// Field is the path of the property in the JSON naming of the
	// specification, such as "intervals[0].payloads[1].type".
	Field  string
	Reason string
}

ValidationError reports a property that violates the OpenADR schema. It is returned by the Validate methods, by the clients before a request is sent, and by the wire codecs for malformed messages.

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) Is added in v0.4.0

func (e *ValidationError) Is(target error) bool

Is matches ErrInvalid.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value is one data point of a ValuesMap: a number, a string, a boolean or a Point. The zero Value is invalid. Values are comparable with ==.

JSON does not distinguish integers from other numbers, so both are held as a float64; integers beyond 2^53 lose precision.

func Bool

func Bool(b bool) Value

Bool returns a boolean Value.

func Number

func Number(n float64) Value

Number returns a numeric Value.

func PointValue

func PointValue(x, y float64) Value

PointValue returns a Value holding the point (x, y).

func String

func String(s string) Value

String returns a string Value.

func (Value) Bool

func (v Value) Bool() (bool, bool)

Bool returns the boolean held and whether the Value is a boolean.

func (Value) Kind

func (v Value) Kind() ValueKind

Kind reports what the Value holds; it is 0 for the zero Value.

func (Value) Number

func (v Value) Number() (float64, bool)

Number returns the number held and whether the Value is a number.

func (Value) Point

func (v Value) Point() (Point, bool)

Point returns the point held and whether the Value is a point.

func (Value) String

func (v Value) String() string

String formats the Value for diagnostics.

func (Value) Text

func (v Value) Text() (string, bool)

Text returns the string held and whether the Value is a string.

type ValueKind

type ValueKind uint8

ValueKind is the JSON type of a Value.

const (
	KindNumber ValueKind = iota + 1
	KindString
	KindBool
	KindPoint
)

Kinds of value a ValuesMap may hold.

func (ValueKind) String

func (k ValueKind) String() string

type ValuesMap

type ValuesMap struct {
	Type   string
	Values []Value
}

ValuesMap is a list of values together with a type that says what they are, such as "PRICE". Type is one of the strings enumerated in the specification or a privately defined one; this package does not restrict it.

type Version

type Version string

Version identifies a revision of the OpenADR 3 specification.

const (
	// Version310 is OpenADR 3.1.0 (document 20250807-X).
	Version310 Version = "3.1.0"
)

Supported protocol revisions.

Directories

Path Synopsis
Package auth holds what the VTN needs to authenticate and authorize a request: the identity behind a bearer token and the OAuth2 scopes of OpenADR 3.
Package auth holds what the VTN needs to authenticate and authorize a request: the identity behind a bearer token and the OAuth2 scopes of OpenADR 3.
Package client holds the two clients of a VTN: VEN, for a virtual end node, and BL, for the Business Logic of a utility or aggregator.
Package client holds the two clients of a VTN: VEN, for a virtual end node, and BL, for the Business Logic of a utility or aggregator.
Package discovery finds VTNs on the local network, and lets a VTN be found, with DNS-SD over Multicast DNS as OpenADR 3.1.0 describes it ("Discovery and Configuration of Local VTNs"): the service type is _openadr3._tcp, and a TXT record carries the version, the base path and URL of the API, the program names and whether the VTN authenticates.
Package discovery finds VTNs on the local network, and lets a VTN be found, with DNS-SD over Multicast DNS as OpenADR 3.1.0 describes it ("Discovery and Configuration of Local VTNs"): the service type is _openadr3._tcp, and a TXT record carries the version, the base path and URL of the API, the program names and whether the VTN authenticates.
Package e2e holds end-to-end tests of go-openadr against itself: the clients of this module against the VTN of this module, over real HTTPS connections on loopback, with the token service, the stores, the webhook sender and handler, and the MQTT publisher and subscriber that the module ships.
Package e2e holds end-to-end tests of go-openadr against itself: the clients of this module against the VTN of this module, over real HTTPS connections on loopback, with the token service, the stores, the webhook sender and handler, and the MQTT publisher and subscriber that the module ships.
examples
discovery command
Discovery shows how a VEN at a customer site finds the VTN there: the VTN announces itself on the local network with DNS-SD over Multicast DNS, as service type _openadr3._tcp, and the VEN asks the network.
Discovery shows how a VEN at a customer site finds the VTN there: the VTN announces itself on the local network with DNS-SD over Multicast DNS, as service type _openadr3._tcp, and the VEN asks the network.
notifications command
Notifications shows a VEN that is told of new events instead of polling for them: it runs a webhook, subscribes to events at the VTN, and the VTN calls the webhook when Business Logic publishes one.
Notifications shows a VEN that is told of new events instead of polling for them: it runs a webhook, subscribes to events at the VTN, and the VTN calls the webhook when Business Logic publishes one.
observability command
Observability shows what a program can know about a client and a VTN beside what their calls return: the hooks for metrics and for logging, the retry policy, and the errors to tell failures apart by.
Observability shows what a program can know about a client and a VTN beside what their calls return: the hooks for metrics and for logging, the retry policy, and the errors to tell failures apart by.
prices command
Prices runs a VTN that publishes a public tariff to anyone who asks, as the specification allows for public information ("Non-Authenticating VTNs"), and a client that reads it without credentials and lays the prices out as a table.
Prices runs a VTN that publishes a public tariff to anyone who asks, as the specification allows for public information ("Non-Authenticating VTNs"), and a client that reads it without credentials and lays the prices out as a table.
quickstart command
Quickstart runs the basic OpenADR 3 workflow in one process: Business Logic creates a program and an event on a VTN, a VEN discovers them and sends a report, and Business Logic reads the report.
Quickstart runs the basic OpenADR 3 workflow in one process: Business Logic creates a program and an event on a VTN, a VEN discovers them and sends a report, and Business Logic reads the report.
resourcegroups command
Resourcegroups shows resource groups: Business Logic arranges the resources of its VENs by street and district, and each VEN sees the groups its own resources are in.
Resourcegroups shows resource groups: Business Logic arranges the resources of its VENs by street and district, and each VEN sees the groups its own resources are in.
tariff command
Tariff is Business Logic for the command line: it publishes a schedule from a CSV file, such as a spreadsheet exports, as an event of a program, creating the program when there is none of that name.
Tariff is Business Logic for the command line: it publishes a schedule from a CSV file, such as a spreadsheet exports, as an event of a program, creating the program when there is none of that name.
ven command
Ven is a small VEN for the command line: it finds or is told a VTN, registers itself, and prints what the events it may see ask of it, as a table.
Ven is a small VEN for the command line: it finds or is told a VTN, registers itself, and prints what the events it may see ask of it, as a table.
vtn command
Vtn runs a standalone VTN with its own token endpoint, for trying out clients and for interoperability tests.
Vtn runs a standalone VTN with its own token endpoint, for trying out clients and for interoperability tests.
websocket command
Websocket shows notifications over a WebSocket: a VEN subscribes to the events of a program and is told of them on a connection it opened itself, so it needs no address the VTN can reach (as a webhook does) and no broker (as MQTT does).
Websocket shows notifications over a WebSocket: a VEN subscribes to the events of a program and is told of them on a connection it opened itself, so it needs no address the VTN can reach (as a webhook does) and no broker (as MQTT does).
internal
mqtt
Package mqtt is a client for MQTT 3.1.1 (OASIS standard, also ISO/IEC 20922), with what the MQTT binding of OpenADR notifications needs: publishing and subscribing at QoS 1 over TCP or TLS, with a clean session.
Package mqtt is a client for MQTT 3.1.1 (OASIS standard, also ISO/IEC 20922), with what the MQTT binding of OpenADR notifications needs: publishing and subscribing at QoS 1 over TCP or TLS, with a clean session.
mqtttest
Package mqtttest is a small MQTT 3.1.1 broker for the tests and examples of this module: enough of a broker to carry notifications between a publisher and subscribers in one process.
Package mqtttest is a small MQTT 3.1.1 broker for the tests and examples of this module: enough of a broker to carry notifications between a publisher and subscribers in one process.
wire
Package wire defines what a protocol revision has to provide to the client and server: the JSON form of each object.
Package wire defines what a protocol revision has to provide to the client and server: the JSON form of each object.
wire/v310
Package v310 is the JSON representation of OpenADR 3.1.0, following the schemas of the specification's OpenAPI document (20250807-X).
Package v310 is the JSON representation of OpenADR 3.1.0, following the schemas of the specification's OpenAPI document (20250807-X).
ws
Package ws is the part of the WebSocket protocol (RFC 6455) that notifications need: the opening handshake of a server and of a client, text messages, ping, pong and close.
Package ws is the part of the WebSocket protocol (RFC 6455) that notifications need: the opening handshake of a server and of a client, text messages, ping, pong and close.
Package interop holds tests that run this module's clients against VTNs implemented by others.
Package interop holds tests that run this module's clients against VTNs implemented by others.
mdnsprobe command
Mdnsprobe puts package discovery on a network for the interoperability tests: it announces a VTN until it is stopped, or looks for VTNs and prints what it found as JSON.
Mdnsprobe puts package discovery on a network for the interoperability tests: it announces a VTN until it is stopped, or looks for VTNs and prints what it found as JSON.
notification
mqtt
Package mqtt is the MQTT binding of OpenADR 3 notifications, for both sides: Publisher sends a VTN's notifications to a broker, and Subscriber receives them in a client.
Package mqtt is the MQTT binding of OpenADR 3 notifications, for both sides: Publisher sends a VTN's notifications to a broker, and Subscriber receives them in a client.
webhook
Package webhook is the webhook binding of OpenADR 3 notifications, for both sides: Sender delivers a VTN's notifications to the callback URLs of its subscriptions, and Handler receives them in a client.
Package webhook is the webhook binding of OpenADR 3 notifications, for both sides: Sender delivers a VTN's notifications to the callback URLs of its subscriptions, and Handler receives them in a client.
websocket
Package websocket receives the notifications of a VTN over a WebSocket.
Package websocket receives the notifications of a VTN over a WebSocket.
Package server is an OpenADR 3.1.0 VTN as an http.Handler.
Package server is an OpenADR 3.1.0 VTN as an http.Handler.
storage
Package storage is the persistence boundary of the VTN.
Package storage is the persistence boundary of the VTN.
storage/storagetest
Package storagetest is a test suite for implementations of storage.Store.
Package storagetest is a test suite for implementations of storage.Store.
Package timeline turns the intervals of an event or a report into a table: one row per period of time and payload, with the start and end worked out.
Package timeline turns the intervals of an event or a report into a table: one row per period of time and payload, with the start and end worked out.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL