# Relynk meters

Listing EOS energy, water and waste meters, and which meters are sub-meters of which.

Each function is a POST to the path shown, relative to the connector's base URL, with its body as JSON; the response is JSON. The types below are JSDoc.

Notes:
- Add `?tenantId=<id>` to every call when `GET /me` lists more than one tenant for the user.
- Errors are `{ "error": { "code", "message" } }`; a 400 message says how to change the request.
- Lists page with `nextCursor` (or `offset` and `hasMore`). Times are Europe/Oslo, ranges half-open `[from, to)`, ids are UUIDs. `null` means no data.
- A path never changes its contract; a new version gets a new path.

## POST /meters.list/v1

EOS meters (energy, water and waste metering: electricity, district heating and cooling, gas, water, waste) with their carrier, unit, whether they count in building totals (building meters) or are sub-meters, virtual (calculated from other meters) or tenant (lease) meters, what they are used for, and until when EOS has processed them. Filter by building, carrier, id, name, buildingMetersOnly or virtual. Feed the ids to consumption.meters; for a building total use consumption.buildings, which sums exactly the building meters. Pages by id: pass nextCursor back until it is null.

Body: `MetersListInput`. Response: `MetersListOutput`.

```js
/**
 * @typedef {Object} MetersListInput
 * @property {string} [buildingId] A building id (from buildings.list). Matches the building itself and everything that is part of it (uuid)
 * @property {Array<"electricity" | "gas" | "thermal" | "thermal_heat" | "thermal_cool" | "water" | "waste">} [carriers] Only these carriers; omit for all (min items 1, max items 7)
 * @property {Array<string>} [ids] Only these meters (min items 1, max items 1000)
 * @property {boolean} [buildingMetersOnly=false] Only the meters EOS sums into building totals
 * @property {boolean} [virtual] true: only virtual meters, false: only physical ones; omit for both
 * @property {string} [search] Only names containing this text, ignoring case (min length 1, max length 200)
 * @property {number} [limit=500] (min 1, max 1000)
 * @property {string} [cursor] nextCursor from the previous page; omit for the first page (uuid)
 */

/**
 * @typedef {Object} MetersListOutput
 * @property {Array<MetersListOutputMetersItem>} meters (max items 1000)
 * @property {string | null} nextCursor Pass as cursor for the next page; null on the last
 */

/**
 * @typedef {Object} MetersListOutputMetersItem
 * @property {string} id
 * @property {string} name displayName when set, else the resource name
 * @property {string} typeKey
 * @property {string} typeName
 * @property {"electricity" | "gas" | "thermal" | "thermal_heat" | "thermal_cool" | "water" | "waste"} carrier
 * @property {"kWh" | "L" | "kg"} unit The unit of all its consumption: kWh for energy, L for water, kg for waste
 * @property {boolean} isBuildingMeter Counted in its building’s EOS total. Sub-meters are not: adding them to a building meter counts the same use twice
 * @property {boolean} isVirtual Calculated by EOS from other meters (meters.topology shows how)
 * @property {string | null} buildingId
 * @property {string | null} buildingName
 * @property {string | null} consumedForUse What its consumption is used for, e.g. "Ventilation"; null when not set
 * @property {string | null} wasteFraction Waste meters: the fraction it weighs
 * @property {boolean | null} countAsSortedWaste Waste meters: counted as sorted in the sorting grade; null when not set
 * @property {MetersListOutputMetersItemLease1 | null} lease A tenant meter: the lease it meters, whose period clips its values
 * @property {string | null} sourcePointId The point EOS reads it from; its raw readings are not consumption. Null for virtual meters
 * @property {string | null} processedUntil Readings up to this time (Oslo, with offset) are processed; later consumption is not available yet. Null before the first run
 */

/**
 * @typedef {Object} MetersListOutputMetersItemLease1
 * @property {string} id
 * @property {string} name
 * @property {string | null} start
 * @property {string | null} end
 */
```

## POST /meters.topology/v1

How a building's EOS meters (energy, water, waste) are connected: every meter with its carrier and whether it counts in the building total, the sub-meter tree (parent → child; a parent's consumption includes its children's, and the part no child measures is not sub-metered), what each virtual meter is calculated from with its factor, and overlappingBuildingMeters: virtual building meters fed by another building meter of the same building and carrier, or by its sub-meters, which EOS (like its dashboard) counts both, so the building total includes that consumption twice. Use it to explain a building's metering, to break a total down by sub-meter, and before trusting a building total.

Body: `MetersTopologyInput`. Response: `MetersTopologyOutput`.

```js
/**
 * @typedef {Object} MetersTopologyInput
 * @property {string} buildingId A building id (from buildings.list) (uuid)
 * @property {Array<"electricity" | "gas" | "thermal" | "thermal_heat" | "thermal_cool" | "water" | "waste">} [carriers] Only these carriers; omit for all (min items 1, max items 7)
 */

/**
 * @typedef {Object} MetersTopologyOutput
 * @property {string} buildingId
 * @property {Array<MetersTopologyOutputMetersItem>} meters The building's meters, and the meters elsewhere its virtual meters are fed from
 * @property {Array<MetersTopologyOutputSubMetersItem>} subMeters
 * @property {Array<MetersTopologyOutputVirtualSourcesItem>} virtualSources
 * @property {Array<MetersTopologyOutputOverlappingBuildingMetersItem>} overlappingBuildingMeters
 */

/**
 * @typedef {Object} MetersTopologyOutputMetersItem
 * @property {string} id
 * @property {string} name
 * @property {string} typeKey
 * @property {"electricity" | "gas" | "thermal" | "thermal_heat" | "thermal_cool" | "water" | "waste"} carrier
 * @property {"kWh" | "L" | "kg"} unit
 * @property {boolean} isBuildingMeter Counted in its building’s EOS total
 * @property {boolean} isVirtual
 * @property {string | null} buildingId Another building than the one asked for when a virtual meter is fed from there
 * @property {string | null} consumedForUse
 * @property {string | null} wasteFraction
 */

/**
 * @typedef {Object} MetersTopologyOutputSubMetersItem
 * @property {string} parentId
 * @property {string} childId
 */

/**
 * @typedef {Object} MetersTopologyOutputVirtualSourcesItem
 * @property {string} virtualMeterId
 * @property {string} sourceMeterId
 * @property {MetersTopologyOutputVirtualSourcesItemFactor} factor
 */

/**
 * @typedef {Object} MetersTopologyOutputVirtualSourcesItemFactor
 * @property {"implicit" | "static" | "dynamic" | "invalid"} kind implicit: the whole source (×1); static: × value; dynamic: × the share of the building area derivedFrom has (areaKey), worked out by EOS; invalid: EOS cannot use the edge
 * @property {number | null} value static: the multiplier, from -1 to 1
 * @property {string | null} areaKey
 * @property {string | null} derivedFromId
 * @property {string | null} derivedFromName
 */

/**
 * @typedef {Object} MetersTopologyOutputOverlappingBuildingMetersItem
 * @property {string} virtualMeterId
 * @property {"electricity" | "gas" | "thermal" | "thermal_heat" | "thermal_cool" | "water" | "waste"} carrier
 * @property {Array<string>} sourceMeterIds Building meters of the same building and carrier it is calculated from, directly or through their sub-meters
 */
```