Two-layer data model for OpenADR 3 entities.
Raw layer: camelCase keys, string values — direct from the API JSON. Coerced layer: namespaced keywords, ZonedDateTimes, Durations, tick intervals.
Every coerced entity preserves the original raw data as :openadr/raw metadata.
Coercion of ValuesMap payloads is extensible via the coerce-payload multimethod,
dispatching on the payload :type string.
Schemas live in dedicated namespaces: openadr3.entities.schema — coerced entity schemas (the public contract) openadr3.entities.schema.raw — raw API schemas (boundary validation)
Two-layer data model for OpenADR 3 entities. Raw layer: camelCase keys, string values — direct from the API JSON. Coerced layer: namespaced keywords, ZonedDateTimes, Durations, tick intervals. Every coerced entity preserves the original raw data as :openadr/raw metadata. Coercion of ValuesMap payloads is extensible via the `coerce-payload` multimethod, dispatching on the payload :type string. Schemas live in dedicated namespaces: openadr3.entities.schema — coerced entity schemas (the public contract) openadr3.entities.schema.raw — raw API schemas (boundary validation)
(->event raw)Coerce a raw Event map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw Event map into a namespaced entity. Attaches :openadr/raw metadata.
(->interval raw)Coerce a raw interval map (used in events and reports).
Returns: :openadr.interval/id — integer :openadr.interval/interval-period — coerced IntervalPeriod (when present) :openadr.interval/payloads — vector of coerced payload maps
Attaches :openadr/raw metadata.
Coerce a raw interval map (used in events and reports). Returns: :openadr.interval/id — integer :openadr.interval/interval-period — coerced IntervalPeriod (when present) :openadr.interval/payloads — vector of coerced payload maps Attaches :openadr/raw metadata.
(->interval-period raw)Coerce a raw intervalPeriod map.
Returns a map with: :openadr.interval-period/start — ZonedDateTime (or nil) :openadr.interval-period/duration — Duration (or nil) :openadr.interval-period/randomize-start — Duration (or nil) :tick/beginning — ZonedDateTime (when both start and duration present) :tick/end — ZonedDateTime (when both start and duration present)
The entity is directly usable as a tick interval when both start and duration are present. Attaches :openadr/raw metadata.
Coerce a raw intervalPeriod map. Returns a map with: :openadr.interval-period/start — ZonedDateTime (or nil) :openadr.interval-period/duration — Duration (or nil) :openadr.interval-period/randomize-start — Duration (or nil) :tick/beginning — ZonedDateTime (when both start and duration present) :tick/end — ZonedDateTime (when both start and duration present) The entity is directly usable as a tick interval when both start and duration are present. Attaches :openadr/raw metadata.
(->notification raw)(->notification raw extra-meta)Coerce a raw notification into a namespaced entity.
Handles both formats:
The VTN-RI snake_case format is a known bug where MQTT notifications use snake_case instead of the camelCase defined in the OpenADR 3 specification.
Optional extra-meta map is merged into the metadata. Use this to record the delivery channel, e.g. {:openadr/channel :mqtt :openadr/topic "programs/create"}. A future webhook handler would pass {:openadr/channel :webhook} instead.
Attaches :openadr/raw metadata (plus any extra-meta).
Coerce a raw notification into a namespaced entity.
Handles both formats:
- Spec-compliant (camelCase): :objectType, :operation, :object with camelCase keys
- VTN-RI (snake_case): :object_type, :operation, :object with snake_case keys
The VTN-RI snake_case format is a known bug where MQTT notifications use
snake_case instead of the camelCase defined in the OpenADR 3 specification.
Optional extra-meta map is merged into the metadata. Use this to record
the delivery channel, e.g. {:openadr/channel :mqtt :openadr/topic "programs/create"}.
A future webhook handler would pass {:openadr/channel :webhook} instead.
Attaches :openadr/raw metadata (plus any extra-meta).(->object-operation raw)Coerce a raw objectOperation map.
Attaches :openadr/raw metadata.
Coerce a raw objectOperation map. Attaches :openadr/raw metadata.
(->program raw)Coerce a raw Program map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw Program map into a namespaced entity. Attaches :openadr/raw metadata.
(->report raw)Coerce a raw Report map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw Report map into a namespaced entity. Attaches :openadr/raw metadata.
(->report-resource raw)Coerce a raw report resource (nested in a Report).
Attaches :openadr/raw metadata.
Coerce a raw report resource (nested in a Report). Attaches :openadr/raw metadata.
(->resource raw)Coerce a raw Resource map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw Resource map into a namespaced entity. Attaches :openadr/raw metadata.
(->subscription raw)Coerce a raw Subscription map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw Subscription map into a namespaced entity. Attaches :openadr/raw metadata.
(->values-map raw)Coerce a raw ValuesMap (as used in attributes/targets). Same as coerce-payload but kept as a separate entry point for clarity.
Coerce a raw ValuesMap (as used in attributes/targets). Same as coerce-payload but kept as a separate entry point for clarity.
(->ven raw)Coerce a raw VEN map into a namespaced entity.
Attaches :openadr/raw metadata.
Coerce a raw VEN map into a namespaced entity. Attaches :openadr/raw metadata.
(->zoned zdt zone-id)Re-zone a coerced ZonedDateTime to the given IANA zone, preserving the same instant. Useful when a value parsed from the wire (zoned to its numeric offset) needs to be presented in a named zone (e.g. your VTN's market zone) so subsequent arithmetic respects DST.
Example: (->zoned (:openadr/created program) (java.time.ZoneId/of "America/Los_Angeles"))
Re-zone a coerced ZonedDateTime to the given IANA zone, preserving the same instant. Useful when a value parsed from the wire (zoned to its numeric offset) needs to be presented in a named zone (e.g. your VTN's market zone) so subsequent arithmetic respects DST. Example: (->zoned (:openadr/created program) (java.time.ZoneId/of "America/Los_Angeles"))
Coerce a raw API entity map based on its :objectType. Returns a namespaced entity with :openadr/raw metadata.
Dispatches on the :objectType string value.
Coerce a raw API entity map based on its :objectType. Returns a namespaced entity with :openadr/raw metadata. Dispatches on the :objectType string value.
Coerce a raw ValuesMap payload based on its :type string. Dispatches on the :type value (e.g. "PRICE", "USAGE"). Returns a coerced map with :openadr.payload/type and :openadr.payload/values.
Extend for custom payload types: (defmethod coerce-payload "MY_TYPE" [raw] (-> {:openadr.payload/type :openadr.payload-type/my-type :openadr.payload/values (mapv my-coercion (:values raw))} (with-meta {:openadr/raw raw})))
Coerce a raw ValuesMap payload based on its :type string.
Dispatches on the :type value (e.g. "PRICE", "USAGE").
Returns a coerced map with :openadr.payload/type and :openadr.payload/values.
Extend for custom payload types:
(defmethod coerce-payload "MY_TYPE" [raw]
(-> {:openadr.payload/type :openadr.payload-type/my-type
:openadr.payload/values (mapv my-coercion (:values raw))}
(with-meta {:openadr/raw raw})))(notification? m)Returns true if the map looks like a notification payload. Detects both spec-compliant camelCase and VTN-RI snake_case formats.
Returns true if the map looks like a notification payload. Detects both spec-compliant camelCase and VTN-RI snake_case formats.
(validate-raw-program raw)Validate a raw program map. Returns nil on success, Malli explanation on failure.
Validate a raw program map. Returns nil on success, Malli explanation on failure.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |