Skip to content
Updated Oct 5, 2026 by Pablo Coufal · Owner: analysisactivefeatureconfluence-migration Edit on GitHub

Gauge Management ​

Business Context ​

Business-Level Definition ​

Gauges are the measurement devices that feed data into the system. Managing gauges means defining:

  • what is measured
  • where it is measured
  • how the measurement relates to a building's total consumption
  • how raw device readings are transformed into reliable consumption figures

Correct gauge configuration is the foundation on which all charts, reports, invoices and energy management decisions rest.

Requirements Definition ​

Gauge management must support:

  • Creating, editing, archiving and restoring gauges of all kinds
  • Attaching identifying information: name, description, medium, direction, purpose, primary source, unit, serial number, supply point identifier, etc.
  • Assigning a gauge to exactly one building node and configuring its contribution to the building total (outputMode). buildingId assignment (physical/administrative ownership of a gauge) is independent from buildingTotal formula membership
    • a gauge may be added as a formula member to another building's total formula
    • cross-building assignment is currently a manual-only operation (automatic detection/suggestion of formula membership, beyond the existing sub-gauge parent case, is out of scope for v1 and may be revisited later)
  • Defining virtual gauge formulas for computed and manual-input subtypes
  • Configuring gauge-level unit conversion coefficients with full version history
  • Displaying admin-editable labels for medium/direction/outputMode combinations — label text itself is managed globally via the GUI Labelling feature (superadmin-only, not tenant-scoped)
  • Configuring data source trust priority per gauge
  • Restricting which reading sources (manual / remote) are accepted per gauge channel
  • Configuring anomaly detection tolerance per gauge
  • Triggering and reviewing building total formula auto-generation when gauge configuration changes
  • Tracking the primary energy source per gauge for EnMS/EnPI reporting and migration compatibility with EM2
  • Exporting the gauge list to a spreadsheet
  • Controlling visibility and editability through a role-based permission model
  • Reading data and manual readings for each gauge are managed in Reading Management. Invoice records linked to a gauge are managed in Invoice Management (both out of scope for this doc).

Acceptance Criteria ​

  • A gauge can be created, edited, archived and restored through the UI and API
  • A gauge belongs to exactly one building node; the assignment cannot be changed after creation without a special administrative role
    • when buildingId is changed (by a permitted user), the system re-evaluates the building total formula for both the source and destination building and re-runs the cross-building dependency check for the moved gauge's parent/child relationships
    • historical readings, consumption and invoice records are not re-attributed by a buildingId reassignment — they resolve their building transitively via the gauge's current buildingId, so a move is reflected retroactively in historical views
  • gauge.kind and gauge.medium:
    • immutable after creation for standard tenant users; both fields are read-only in the edit form and rejected by the API if included in a PATCH request
    • superadmin role can override this restriction when needed — exact permission model to be specified during Users & Access design
  • Virtual gauges have no gauge.outputMode; the field is hidden in the UI and rejected by the API for kind = virtual
  • KVP gauges (medium = kvp) have no outputMode regardless of kind; the field is hidden in the UI and rejected by the API when medium = kvp
  • A channel whose gauge.readingMethod = remote does not accept a manual reading: the manual entry form is hidden for it and the API rejects a reading with source = manual. readingMethod = manual or both accepts one.
  • Archiving a gauge removes it from active lists but preserves all historical readings and consumption records (whether preserved historical data remains visible in reports and charts after archival is controlled per gauge via gauge.showArchivedInReports)
  • When a gauge with gauge.outputMode = include / subtract is created or its outputMode changes:
    • if the change matches one of the recognised patterns (see "buildingTotal formula auto-generation" below), the formula is generated/updated automatically without requiring explicit user confirmation
    • for all other cases (unrecognised pattern, cross-building dependency, no INCLUDE gauge at all), the system raises an alert and offers an updated formula suggestion; the user must explicitly accept or reject/edit the suggestion — there is no option to dismiss the alert without making the decision
  • Gauge-level coefficient changes create a new versioned record; the previous value is retained and used for historical reports
  • The gauge list can be exported to XLSX
  • Multiple gauge records representing channels of the same physical device are visually grouped as a single entry; the user can add a channel to an existing gauge via a dedicated button — the grouping mechanism is transparent and not exposed in the UI

N/A — not available in the source material.

Technical Context ​

User Stories / Use Cases ​

Browse and find gauges: A user opens the gauge list for a building → the list shows all active gauges with columns for name, kind, medium, direction, supplyPointId (EAN/EIC/OM), subscriber name (organisation name), outputMode and last reading date. Archived gauges are listed in a separate section. The user can filter by kind, medium and outputMode; free text search matches gauge name, supplyPointId (EAN/EIC), serialNumber and the organisation's name (subscriberId); the list can be sorted by name, medium, outputMode or last reading date. Connection status is not shown in the gauge list — it is managed in Remote Connection Management (out of scope for this doc).

Create a gauge: A user with the "Create gauge" permission opens the creation form from the gauge list or from a building's gauge tab (building pre-filled) → chooses kind, medium and purpose → fills in placement and identification → saves. The form shows, before saving, how the gauge will affect the building total; when the result is not a recognised pattern the system asks the user to accept or edit the suggested formula. Full flow: Gauge creation flow; specification: zadani-formular-meridla.md.

Edit a gauge: A user edits an existing gauge. kind and medium are immutable after creation and are always read-only. buildingId is read-only for standard users but editable by a user with a specific permission (the building-reassignment permission). Changing buildingId re-evaluates the building total formula for both the source and destination building, and re-runs the cross-building dependency check for any sub-gauge relationships involving the moved gauge. Changes to outputMode or direction follow the same rule as building reassignment: if the resulting configuration matches one of the recognised patterns the formula updates automatically, otherwise the system raises an alert and the user must confirm the suggested formula before it is applied.

Configure a virtual gauge formula: A user creates or edits a virtual gauge of subtype computed → opens the formula editor → selects source gauges and defines the arithmetic expression → saves.

Manage gauge coefficient: A user opens the coefficient section on a gauge detail page → sees the current coefficient value and history → adds a new coefficient record with a validFrom date and the new value. The system recalculates affected Consumption records.

Archive and restore a gauge: A user archives a gauge. The gauge moves to the archived section. Historical data is preserved. A user with the restore permission can reactivate it.

Add a channel to an existing physical meter: A user opens a gauge and clicks "+ Add channel" → a pre-filled form opens with medium locked to the parent's medium and buildingId pre-set → the user fills in direction, EAN/supplyPointId, unit and name → saves. The system transparently handles the grouping on the backend. The user never sees or interacts with any grouping ID.

View building total formula: A user opens a building detail page → clicks "Building total formula" → sees the current formula, all formula members with their current values, and an edit button for manual adjustment. The system-managed virtual gauges are not visible in the regular gauge list.

UI/UX Design ​

Figma (✅ Approved) covers the building's gauge tab and the gauge card's readings and invoicing tabs — not creation. The gauge form is a detail form with a side panel (the design system's Detail with side panel template, as on the building): one card for creating and editing, edited in place with no separate read page, the left panel listing the building's gauges by medium with sub-gauges under their parent. One ordering rule shapes it: a field that changes the form sits above the fields it changes — the classification section (Zařazení měřidla) holds level, meter type, parent gauge or virtual subtype, supply type, the distribution rate (retail only) or the NT / ST tariff switches (VN / VVN), the water source and payment scope, so nothing below changes shape once it is filled in; and the measured parameters sit right after it because the chosen channels decide what the supply-point section shows (the export EAN and electricity sharing appear only with a Dodávka do sítě channel, the production EAN only with a Výroba channel). The form has six sections (decided 5 Oct 2026): Zařazení měřidla → Měřené parametry → Odběrné místo (four sub-sections: supply-point identification, supply parameters, metering device, energy-purchase data of the supply point) → Odběratel → Fakturace → Smluvní ceny (a read-only section of its own, fed from the contract). Measured parameters are channels added through + Přidat parametr (the connector's unpaired endpoints first, then the medium's parameters), each with a manual, remote or dual reading source carrying its own unit, frequency, coefficient and — on the manual source — the frequency check; every consumption channel carries its usage type (default Neurčeno, a Rozdělit… link to the Rozdělení užití modal); the effect on the building total is a button and modal, Vliv OM na spotřebu objektu. Sub-gauges inherit from the parent and choose their effect on the building total; virtual gauges carry a formula editor or a single manual parameter. The field-level specification is zadani-formular-meridla.md (Czech), the screens and their sources are in design/, the EM2 mapping in migrace-em2.md, the story breakdown in stories-breakdown.md (Czech). One card on the gauge detail is designed separately: the calorific value card, shown only for gauges whose medium converts volume or mass to energy — see Calorific Value Management — UI/UX Design. Earlier rounds and one-off reviews are in _archive/.

Per-record history wireframes. The per-row history icon on the Odečty and Fakturace tables opens that record's own change history (reading, invoice respectively) — the shared destination view described in Audit Log — Per-Record History component. Gauge Detail carries both patterns: a header "Historie" button for the whole gauge record, matching Building Detail / Energy Management, and per-field "Historie" links (Distribuční sazba or the NT / ST switches, Účel užití of a channel, Výrobní číslo měřidla, Počet fází × jistič) for parameters with their own change history. Recreated as clickable pages: Gauge Detail, Odečty and Fakturace.

Functional Requirements ​

Gauge kind + subtype ​

Each gauge has a kind that determines its structural role:

KindPhysical deviceoutputModeIn gauge listExample
standardYesRequired (except medium = kvp)YesMain electricity meter, gas meter
subGaugeYes (sub-circuit)Required (except medium = kvp)YesFloor-level electricity sub-meter
virtual / computedNoNoneYesFVE self-consumption formula
virtual / manualInputNoNoneYesCost allocation from landlord invoice
virtual / buildingTotalNoNoneNo (via building detail)Campus total consumption

Medium, direction and purpose ​

Each gauge carries a medium identifying what physically flows through the device. Medium is immutable after creation. The direction follows the DLMS/COSEM convention; however, the user never selects import / export values directly — the UI presents context-sensitive human-readable options per medium.

MediumUI option (EN / CZ)direction storedTypical reading typeoutputMode applicable
electricityConsumed from grid / Odebráno ze sítěimportenergy, powerYes
electricityReturned to grid / Dodáno do sítěexportenergy, powerYes
electricityProduction (FVE, CHP, …) / Výrobaexportenergy, powerYes — typically reportOnly
electricityBattery charging / Nabíjení baterieimportenergy, powerYes — typically reportOnly
electricityBattery discharging / Provoz na bateriiexportenergy, powerYes — typically reportOnly
gasConsumption / Spotřebaimportenergy, volumeYes
waterConsumption / SpotřebaimportvolumeYes
heatConsumption / SpotřebaimportenergyYes
heatProduction (KGJ, …) / Výroba teplaexportenergyYes — typically reportOnly
fuelConsumption / SpotřebaimportvolumeYes
phmConsumption / SpotřebaimportvolumeYes
kvpTemperature / TeplotanulltemperatureNo — always null
kvpHumidity / VlhkostnullhumidityNo — always null
kvpCO₂ concentration / Koncentrace CO₂nullco2No — always null
  • for kvp the UI does not show a direction selector (always stored as null)
  • gas meters can produce readings of type energy (kWh, if the device converts inline) or volume (m³, converted to energy later using the medium's calorific value) — the medium is gas in both cases
  • the unit a user types when entering a manual reading (e.g. MWh) may differ from the gauge's canonical unit (e.g. kWh) — the frontend converts to the canonical unit before sending to the backend
  • the "outputMode applicable" column applies to gauges of kind = standard or subGauge with medium ≠ kvp; virtual gauges never have outputMode regardless of medium
  • UI labels for each combination (medium, direction, outputMode) are managed via the GUI Labelling feature (not by a gauge-specific mechanism)
    • editing is superadmin-only, not a tenant-level admin capability — a change applies globally, across all tenants
    • changing the label does not require a new deployment
  • the underlying technical combinations (kind × medium × direction × outputMode) remain enum-driven and require developer involvement to extend — they are tied to buildingTotal formula logic and cannot be safely opened up for editing; combinations offered to the user (how many wording variants exist for each, and their display text) are all superadmin-editable without deployment — see the gaugePurposeOption mechanism below
  • gauge.labelOverride (a per-gauge custom label on top of the GUI Labelling default) is retired by the 23 Sep design round — it had no use in practice; the label always comes from GUI Labelling

Purpose-driven gauge creation ​

To keep gauge creation simple, the user only provides necessary inputs on the creation form: gauge.kind (standard / sub-gauge / etc.), gauge.medium (what it measures), and a word choice from a context-sensitive list. Everything else (direction, outputMode, purposeCategory, etc.) is set automatically. Multiple rows may point to the same target combination (e.g. "Odebráno ze sítě" and "Spotřeba" can lead to an identical stored configuration), so a superadmin can offer whichever variants make sense to different users without creating any ambiguity in the underlying data.

  • gauge.purpose stores a reference to the specific gaugePurposeOption the user picked — not just the resulting combination (required in order to be able to reconstruct the original selection)
  • adding a new wording variant that targets an existing, valid combination is a superadmin (no-deployment) operation — a new gaugePurposeOption row
  • adding an entirely new combination (a direction/outputMode pairing that isn't already valid) still requires developer involvement — tied to enum values and buildingTotal formula logic

Worked example — two wordings, one configuration

translationKeykindmediumtargetDirectiontargetOutputModepurposeCategoryisDefaultForCombination
gauge.purposeOption.consumedFromGridstandardelectricityimportincludeconsumedFromGridtrue
gauge.purposeOption.consumedFromGridAltstandardelectricityimportincludeconsumedFromGridfalse

Both options resolve to the identical stored gauge configuration. The user only ever sees two buttons with different wording; the system stores the same direction/outputMode either way; gauge.purpose records exactly which of the two rows was clicked; re-opening the gauge later shows the originally chosen wording.

The primarySource (gauge.primarySource) records where the energy physically originates, independently of the gauge's role in the building's energy balance. It complements purpose in cases where purpose alone is ambiguous: for example, a battery charging gauge (purpose = batteryCharging) can be charged from the grid or from solar production, and primarySource captures this distinction. The field is carried over from EM2 (primary_source column on gauge_meter, PHP constants SOURCE_* in GaugeType.php) for full migration compatibility and is used in EnMS/EnPI reporting. It is optional — null is valid for sub-gauges and KVP gauges; null is also acceptable for standard consumption gauges where the source is implied by context.

Gauge creation flow ​

The form is a detail form with a side panel (see UI/UX Design). The full field list, requiredness, EM2 origin and the per-kind matrix are in zadani-formular-meridla.md.

Kind first, then everything that shapes the form. The kind is the first choice after the building; the classification section also carries the supply type and the tariff / TDD class for main gauges, so that the rest of the form is fixed once it is filled in:

KindWhat changes
standardMedium chosen; supply point identifier required and labelled by medium; supply-point, subscriber, billing and contractual-price sections shown; effect on the building total derived from the measured parameters.
subGaugeParent gauge required right after the level; meter type (medium), primary source and billing unit are taken from the parent and locked; supply-point identification and parameters, subscriber and tariff fields hidden (the parent's supply point is shown read-only in the metering-device sub-section), billing reduced to a link to the parent; the user chooses the effect on the building total in the classification section (none — default in the parent's building / subtract / add, stored as outputMode reportOnly / subtract / include); the usage allocation of the parent channel is copied at creation and editable afterwards, never live-inherited.
virtualSubtype (formula / manual input) right after the meter type, since it swaps the middle of the form; identification is name and comment only; for a formula the measured parameters are replaced by the formula editor — members of the same medium with a + / − sign, from this or another building of the client, Platnost od, the result unit locked to the meter type, formula history; manual input keeps one parameter, unit, entry frequency and its own usage type; a formula carries no usage (the split follows its members); no supply point, subscriber or billing; never part of the building total.

Changing the kind before saving clears fields the new kind does not use (with a confirmation when they were filled). After saving, kind, medium and supply point identifier are fixed.

Measured parameters. At least one; each ticked parameter creates one gauge record, all sharing one physicalMeter. Per parameter: reading method (manual / remote / both) and, for production, production type; per reading source: unit, reading frequency, coefficient (unit and coefficient versioned) and the manual frequency check. Usage type is a property of the channel, not of the meter (decision R1, 1 Oct 2026): every consumption channel has a Účel užití row with a select (default Neurčeno — nothing is pre-filled, D01), an offer filtered by medium and channel meaning from the platform catalogue usageType, and a Rozdělit… link to the allocation modal — see Usage allocation below. Export and production channels carry no usage in v1 (K3); KVP channels never. The UI never says "purpose" — that word is reserved for the channel meaning. Production adds to the building total, export subtracts. The tariff series a channel records (total, or VT / NT / ST) are not shown in the form; they follow the tariffSeries rule from the gauge's supply type, distribution rate or tariff switches at the reading time (specification, chapter 3.2).

Supply point. One section with four sub-sections — supply-point identification (identifier, export and production EAN shown only when the matching channel exists, electricity sharing, supply-point number, distributor, portal link), supply parameters (measurement type, breaker, reserved capacities and regulation levels for VN / VVN, daily reserved capacity and calorific value for gas, calorific value and installed power for fuel), metering device (serial number with history, type / model, placement, owner, photo) and the energy-purchase data of the supply point (annual consumption in its four shapes, additional conditions, low-tariff specification, consumption diagram). Billing-side energy-purchase fields (Spadá do hromadného nákupu, Odpovědnost za odchylku) sit in Fakturace; contractual prices are a section of their own, read-only from the contract.

Not in the form: readingMode (derived per reading source), allowsManualReading (replaced by readingMethod on the channel), labelOverride (retired), a meter-level usage type (replaced by the channel allocation), Povolit záporné hodnoty (follows the channel direction), Zobrazovat po archivaci (an option of the archive dialog), Druh paliva (it is the primary energy source), the global manual-frequency switch (per manual source now), and any PXE switch (feature id26 knows none). On save the building total is re-evaluated in the same transaction; an alert opens the confirmation dialog (accept suggestion / edit manually, no dismiss). Model changes this requires are listed in the specification, chapter 7; the EM2 field mapping and the migration tasks are in migrace-em2.md.

Energy-purchase attributes (Confluence feature id26 — PXE) ​

The gauge detail carries the attributes the joint energy purchase needs. They appear on every main electricity and gas meter — there is no switch that turns them on — and follow the supply type. The existing flag isPartOfBulkPurchase (Spadá do hromadného nákupu, in Fakturace) does not change the form; it only decides whether the supply point lands in the table sent to PXE from the Sdružený nákup energie tab.

  • Odběrné místo — supply parameters — measurement type (now for every supply type), and for electricity VN / VVN the annual reserved capacity and reserved power in MW, the monthly reserved capacities and the safety minimum with regulation levels (a description plus an Editace modal).
  • Odběrné místo — energy-purchase data — annual consumption in four shapes: VT and NT for a dual-tariff low-voltage meter, drawn as one field (two values, one hint, one Načíst z databáze button that fills both, attributes 13 and 14); a single value with the same button for electricity VN / VVN and low-pressure gas (attribute 15); and, for gas střed./velkoodběr, a read-only value computed from monthly figures with an Editace button opening the Měsíční hodnoty spotřeby modal — twelve values in MWh and a yearly total (attribute 16). Additional supply-point conditions (7), the low-tariff specification (18, VN / VVN with the low tariff on) and the consumption diagram (17: the button reads Vybrat z nahraných dokumentů until a file is assigned and Stáhnout afterwards, with the file name and data resolution below it) sit here too.
  • Fakturace — billing type and name, deposit type and size, the extraordinary settlement date, the billing note, Spadá do hromadného nákupu and, for VN / VVN and gas střed./velkoodběr, the deviation responsibility (12).
  • Smluvní ceny — the contract type (6), in the read-only contractual-price section.
  • Odběratel — the billing contact person; the contact e-mail becomes "Další e-mail pro zasílání vyúčtování" and the bank account and IČO are read-only, fed from the organisation.

The PXE-side validation (minimum purchased volume, diagram availability for VN / VVN) and the Sdružený nákup energie tab in Přehledy are outside this form — feature id26 owns them.

Usage allocation (účel užití) ​

What the measured consumption is used for is a property of the channel, not of the physical meter, and it may be a split (decisions R1–R3, 1 Oct 2026; rules D01–D09 confirmed by the product owner). A 4Q meter can have grid consumption as Ostatní provoz and production unclassified; a shared heat meter can be 70 % heating / 30 % hot water. The values come from the platform catalogue usageType (full set in v1: heating, hot water, cooling, ventilation, lighting, base load, cooking, EV charging, transport, sanitary, process water, irrigation, other — R4, R7), each item carrying its category, its climate-normalisation method and the media × channel meanings it may be offered on (the K1 matrix as data; backend and frontend share one offer — N10). Nothing is pre-filled: a new channel is Neurčeno until the user chooses (D01), and Neurčeno is a state, never an item of a split.

The configuration lives in gaugeUsageAllocation + gaugeUsageAllocationItem: per channel, validFrom (the start of a local day, D02), mode — single (one usage, 100 %), estimated (explicit monthly shares per usage, every month exactly 100 %, no remainder — D03) or measured (parts measured by sub-gauge channels inside the parent's balance boundary plus exactly one usage that receives parent − Σ parts — O01) — and append-only versions where a backdated correction supersedes the version it replaces (UC-06). Variant C computes the split at read time from the daily aggregate and the allocation valid on that day (consumption_daily_by_usage); consumption keeps one row per channel and slot, basic charts never change, a corrected share needs no reprocessing, and a split below daily granularity is refused rather than silently ignored (D06). Missing or inconsistent source data leaves the day unassigned with a reason — never zero, never a fallback to the estimate, never a negative remainder (D04, D05, D09); the building summary counts a sub-gauge source once (D08). A sub-gauge copies the parent channel's allocation at creation as its own, editable configuration, with source references only where valid for the child (R5, D07, N07).

In the form the channel row carries the usage select, a Rozdělit… link and, once a split exists, a chip summarising it with Upravit rozdělení; the modal Rozdělení užití holds the three modes, Platí od, the usage × month table with per-month sums, or the measured parts with their sources and the remainder usage (specification, chapters 3.2 and 4.4). Consumption calculation (Epic 3.1) and climate normalisation (Epic 3.2, only hdd / cdd usages are normalised) read the effective allocation per day; buildingCalculatedConsumption.usageType remains an independent input. The supply type (supplyType) likewise lives on the gauge — contract.medium keeps the commercial category of the contract, and a gauge may only be attached to a contract of the matching category. Still to confirm with stakeholders (K1–K4): the offer per medium and its labels (Ostatní provoz vs Ostatní), Teplá voda on a water meter vs Ohřev teplé vody on an energy meter, production / export / sharing outside the v1 split, and the handling of deactivated usages in history.

Supply point identifier ​

Each gauge of kind = standard or subGauge may carry a gauge.supplyPointId — the identifier of the metering point (odběrné místo). The field is medium-aware: the UI presents it with the correct label and applies format validation based on the selected medium. The field is hidden for kvp and virtual gauges.

MediumUI labelFormatRequired
electricityEANExactly 18 digitsYes — for kind = standard and direction = import; a 4Q meter's export channel carries its own EAN
gasEIC16 alphanumeric charactersRecommended
water, heat, fuel, phmIdentifikační číslo OMMax 50 characters, free stringNo
kvp, virtual—Hidden in UINo

The identifier is immutable after creation — it uniquely defines the supply point for its lifetime. Format validation is enforced by both frontend and backend based on the gauge's medium. The value is unique per tenant.

One physical meter, multiple gauge records ​

A single physical device can measure multiple channels — a 4Q electricity meter has a separate channel (and a separate EAN) for import and export. Each channel is a separate gauge record in EM3. This is a backend detail — the user always sees the channels grouped as a single physical device entry.

Grouping is based on gauge.physicalMeterId — a UUID shared by all channels of the same physical device and referencing a dedicated physicalMeter record. This approach is symmetric: no channel is "primary" — all channels are equals. A channel can be archived or deleted without affecting the others.

physicalMeterId is more reliable as a grouping key than serialNumber or supplyPointId because serialNumber can change at any time and each channel of a multi-channel meter has its own supplyPointId.

gauge.serialNumber is a regular gauge attribute:

  • editable at any time directly on the gauge detail
  • subject to the standard gauge-edit permission

Every change writes a new gaugeSerialHistory record (validFrom = now), so serialNumber always mirrors the most recent history record.

Each channel (gauge record) carries its own gauge.readingMethod (manual / remote / both), set on the measured-parameters table:

  • a channel read remotely only (remote) hides the manual entry form in the UI and the API rejects any reading submission with source = manual
  • channels of the same physical meter may differ — e.g. an import channel may accept manual readings as a fallback, while an export channel is remote-only and must never be entered manually

UI — adding a channel

The user never interacts with physicalMeterId or the physicalMeter table directly. The flow is:

  1. The user creates the first channel (e.g. import) as a regular gauge — no mention of physical meter grouping.
  2. On the gauge detail page, a "+ Add channel" button is visible. The user clicks it.
  3. A pre-filled form opens: medium is locked (inherited from the first channel), buildingId is pre-set. The user fills in only the channel-specific fields: direction, supplyPointId, unit, name.
  4. On save, the backend transparently creates or reuses the physicalMeter record and assigns physicalMeterId to both channels. The user sees no confirmation of this — just the updated gauge entry showing both channels.

UI — gauge list presentation

All channels sharing a physicalMeterId appear as a single row in the gauge list, e.g.:

Main electricity meter
   Import  1 200 kWh  |  Export  340 kWh     [+ Add channel]

Opening the detail shows both channels side by side. Each channel can be individually selected for charts and reports. The physicalMeter table is never surfaced.

outputMode and buildingTotal ​

Each gauge of kind standard or subGauge (with medium ≠ kvp) carries a gauge.outputMode that expresses the user's intent about how the gauge contributes to the building total. The system derives the arithmetic sign in the formula by combining outputMode with direction:

outputModedirectionEffect on building total
includeimportAdded (+)
includeexportSubtracted (−)
subtractimportSubtracted (−)
subtractexportAdded (+)
excludeanyIgnored
reportOnlyanyIgnored in total; visible in reports and available in VG formulas

buildingTotal formula auto-generation ​

When a gauge's outputMode is created, changed, or a gauge is archived, the system evaluates all standard and subGauge gauges on the building and applies these rules:

Building hasClassificationResult
≥1 INCLUDE standard gauge + REPORT_ONLY sub-gauges in the same buildingKnown valid pattern — downstream measurementbuildingTotal auto-generated from INCLUDE gauges. No alert.
INCLUDE sub-gauge(s), no INCLUDE standard gaugeKnown valid pattern — third-party buildingbuildingTotal auto-generated from INCLUDE sub-gauges. No alert.
Sub-gauge in Building B has its parent gauge in Building ACross-building dependencyAlert: "Building A total may be incomplete — sub-gauge [name] in Building B draws from [MG1] in Building A."
No INCLUDE gauge at allIncomplete configurationAlert: "Building [name] has no building total defined."

The system offers an updated suggested formula. The user must confirm any change — the formula is never updated silently. Virtual gauges are never included in auto-generation. The confirmation dialog offers exactly two outcomes: accept the suggested formula, or reject/edit it. There is no "dismiss" option; until the user decides, the previous formula remains active and the alert persists.

Building total per medium ​

Auto-generation runs independently per medium. Each medium present in a building generates its own buildingTotal Virtual Gauge (isSystemManaged = true). A building with electricity, gas and water meters will therefore have three separate buildingTotal VGs — one per medium — each derived solely from the INCLUDE gauges of that medium.

Cross-medium aggregation (e.g. total energy consumption in kWh across all media) is not auto-generated. It requires a separate Virtual Gauge (isSystemManaged = false) with an explicit formula using calorific value coefficients defined manually by the user.

Topology-assisted setup ​

When the system can infer from the known building topology that a Virtual Gauge is needed but missing, it offers to create it automatically. The trigger condition: a building node has no direct INCLUDE gauge, but one or more of its child nodes have sub-gauges whose parent gauge is attached to a sibling or ancestor node.

In that case the system offers to create a residual Virtual Gauge for the building node whose consumption can only be derived by subtracting known sub-gauge values from the parent total. This reduces setup effort and eliminates a common configuration mistake (forgetting to define the virtual gauge manually). The user reviews and confirms the suggested formula before it is applied — the system never creates Virtual Gauges silently.

Volume coefficient ​

A gauge-level coefficient corrects what the device reports into the quantity it actually measured — a pulse weight, a transformer ratio, or a correction for rated against actual throughput. It is a property of the meter and its installation, not of what flows through it.

  • these coefficients are versioned — each change creates a new volumeCoefficient record with a validFrom date
  • a gauge carries two independent histories, one for readings entered by hand and one for readings delivered by a remote connection, distinguished by readingSource
  • a coefficient change triggers recalculation of affected Consumption records from the validity date

Converting a volume or mass of fuel into energy is a different question with a different answer: it is a property of the fuel, stated as a calorific value at gauge, client or platform scope — see Calorific Value Management. The gauge-scope value is read and written through GET/POST /v1/gauges/:id/calorific-values, documented there; the gauge detail shows it as its own card. Both are applied before the building-level coefficients in buildingParameter, which Consumption Normalisation manages.

Data source priority ​

Each gauge can override the tenant-level source trust priority via gauge.sourcePriority. The default priority is remote > manual > invoice. All 6 permutations of the three sources are supported. When null, the tenant default applies.

Anomaly detection ​

Automatic anomaly detection is applied at write time for every new Reading or Consumption record. Per-gauge configuration:

  • gauge.allowsNegative — if false (default), negative values are automatically flagged
  • gauge.anomalyToleranceMin / gauge.anomalyToleranceMax — optional per-gauge override of the tenant-level tolerance band
    • stored as two asymmetric percentage-deviation integers (e.g. -20 / +20), not as a single multiplier
    • not editable via UI in v1 — schema-only fields reserved for future per-gauge configuration; no v1 feature sets or displays them
    • when null, the applicable tolerance resolves to the matching clientTolerance record set for the gauge's client + medium (optionally + primarySource)
    • when no matching record exists, the system default (-20/+20) applies. Applies to manual readings in the base product. Remote (DO) readings use a separate advanced control mode configuration (add-on feature).

When a value falls outside the allowed range, the record is automatically flagged with a ReadingTag or ConsumptionTag (category: ACCIDENT) and a Notice action is raised. The raw record is always stored, but the flag prevents it from being included in aggregates until reviewed.

Checks performed on write:

  1. Negative value (controlled by allowsNegative)
  2. Value lower than previous cumulative reading (suspected meter replacement or data error)
  3. Stagnation — same value repeated across N consecutive 15-minute intervals
  4. Null / empty value from remote device
  5. Excessive spike — consumption exceeds rolling average by more than the tolerance multiplier

KVP gauges (Indoor Environment Quality) ​

KVP gauges measure non-energy quantities (temperature, humidity, CO₂). They are physical devices (kind = standard or subGauge) but behave differently from energy media in two important ways:

  • medium = kvp, direction = null always — the UI does not show a direction selector for KVP gauges
  • outputMode = null always — KVP gauges are never part of any building total, regardless of kind. The field is hidden in the UI and rejected by the API when medium = kvp.
  • their readings have type = temperature / humidity / co2 and tariff = null
  • visible in the gauge list and can be used in Virtual Gauge formulas (e.g. for temperature-based normalisation), but they never contribute to building totals
  • for temperature-based normalisation, KVP gauges interact with climate data from WeatherStation Management — a weather station provides external temperature reference for degree-day calculations

Subscriber identification on gauge detail ​

The gauge detail page displays a read-only "Subscriber identification" card showing key attributes of the organisation referenced by gauge.subscriberId. This mirrors the EM2 behaviour on the B3_MeridloDetail tab. All fields are read-only — editing is done via Client Management. The card is hidden when subscriberId is null (virtual gauges, KVP gauges).

Displayed fieldSource attributeNotes
Nameorganisation.nameAlways shown
IČOorganisation.icoShown when non-null
DIČorganisation.dicShown when non-null
Legal formorganisation.legalFormShown when non-null
Registered addressorganisation.registeredStreet + registeredCity + registeredZipShown when any address field is non-null
EnMS contactorganisation.contactEnmsUserIdResolved to user display name + email; shown when non-null
Billing contactorganisation.contactBillingUserIdResolved to user display name + email; shown when non-null
Technical contactorganisation.contactTechnicalUserIdResolved to user display name + email; shown when non-null
Regulatory contactorganisation.contactRegulatoryUserIdResolved to user display name + email; shown when non-null

The card includes a link navigating to the full organisation detail in Client Management. The subscriber organisation entity is defined in Client Management.

Archival behaviour ​

  • A gauge can be archived manually by a user with the appropriate permission, or automatically as a cascade when its parent building is archived. In the cascade case, all gauges belonging to the building are archived atomically in the same transaction using the same archival logic (setting isArchived, archivedAt, and archiveReason); the archiveReason is inherited from the building archival reason.
  • Archiving a gauge sets gauge.isArchived = true, records archivedAt, and requires an archiveReason.
  • When a gauge is archived as part of a building cascade, its archiveReason is set to the same value as the building's archiveReason (direct mapping). All four BuildingArchiveReason values (saleDemolition, transfer, outOfScope, other) have a matching value in GaugeArchiveReason. No conversion logic is needed.
  • Archived gauges are excluded from active lists by default; the showArchivedInReports flag controls whether their historical data appears in reports and charts.
  • Historical readings and consumption records are fully preserved regardless of archival status.
  • If the archived gauge participated in a building total formula, the formula is flagged for review.
  • A gauge with readings or invoice records cannot be permanently deleted — archival is the correct end-of-life action.
  • System-managed gauges (isSystemManaged = true, i.e. buildingTotal virtual gauges) cannot be manually archived — they are archived automatically when all their source gauges are archived.
  • Automatic deactivation of gauges due to long-term user inactivity is intentionally out of scope for EM3 v1.

Per-record history ​

The gauge detail, and the Odečty and Fakturace tabs, each carry a "Historie" entry point onto that record's own change history, reached in place — the same audit trail as Audit Log. See UI/UX Design above for the wireframes.

Permissions model ​

Pending — to be defined during Users & Access design. Access to gauge creation, editing, coefficient management, formula editing, archival and export is controlled by roles. The exact role names and their permission matrix will be specified as part of the Users & Access domain.

Building reassignment (gauge.buildingId) requires a dedicated permission, mirroring EM2's ROLE_CAN_CHANGE_GAUGE_BUILDING — assignable independently of general gauge-edit permissions.

Non-Functional Requirements ​

Performance ​

  • Coefficient recalculation is triggered asynchronously — the API returns 202 Accepted and the recalculation runs as a background job

Transactional Operations ​

  • Gauge creation must be atomic — all succeed or all roll back
  • Coefficient record creation and the recalculation trigger must be atomic — the coefficient is not applied unless the recalculation job is successfully queued

Implementation Notes ​

Virtual gauge formula storage ​

Virtual gauge formulas (for virtualSubtype = computed and buildingTotal) are stored as a linear combination of gauge references — a formula version (gaugeFormula) with its signed members (gaugeFormulaMember). Versions are append-only with a valid-from date, so an edit never rewrites the totals of past intervals; the interval's consumption is evaluated with the version that applied to it and written as consumption of the virtual gauge with source calculated (see Consumption calculation). The formula editor shows members by gauge name with a + / − sign and stores gauge IDs with a ±1 coefficient. A member may be a computed virtual gauge as long as the dependency graph stays acyclic, never the building total itself. When a referenced gauge is archived, the formula is flagged for review.

physicalMeter — backend handling of channel grouping ​

The physicalMeter table is a backend-only grouping entity. It is never exposed in the UI or API responses — the frontend works exclusively with gauge records and uses physicalMeterId only to determine which gauge records belong together for display purposes.

When the user clicks "+ Add channel" on an existing gauge, the frontend sends a standard POST /v1/gauges request with a sourceGaugeId field referencing the existing gauge. The backend then:

  1. Checks whether the referenced gauge already has a physicalMeterId.
  2. If not — creates a new physicalMeter record, assigns its UUID to the existing gauge (PATCH gauge SET physicalMeterId) and to the new gauge being created.
  3. If yes — assigns the existing physicalMeterId to the new gauge.

Both operations (creating/updating the existing gauge and inserting the new gauge) must be wrapped in a single DB transaction — if either fails, both roll back.

The physicalMeterId attribute on gauge is immutable after creation. Reassignment of a channel to a different physical meter is an admin-only operation not exposed in the standard UI.

Manual reading source restriction ​

gauge.readingMethod is enforced at the point where a Reading is written, not only in the gauge configuration UI. Any endpoint that accepts a manual reading (Reading Management, out of scope for this doc) must validate readingMethod for the target gaugeId before insert and reject with an appropriate error when the channel is remote-only. This validation applies uniformly regardless of entry path (manual UI form, bulk import, external API).

Sub-gauge parent validation ​

When creating a subGauge, the API must validate that:

  • The referenced gauge.parentGaugeId exists and has kind = standard
  • The parent gauge belongs to the same tenant

No constraint is placed on which building node the parent gauge belongs to — cross-building sub-gauges are a valid topology (and trigger the cross-building dependency alert described above).

API Analysis ​

Gauge CRUD and lifecycle:

GET    /v1/gauges                    List gauges (filterable by building, kind, medium, outputMode, archived; searchable by name, supplyPointId, serialNumber, subscriber name; sortable by name, medium, outputMode, last reading date)
GET    /v1/gauges/:id                Gauge detail
POST   /v1/gauges                    Create gauge (pass sourceGaugeId to add a channel to an existing physical meter)
PATCH  /v1/gauges/:id                Update gauge fields (a serialNumber change also inserts a gaugeSerialHistory record)
DELETE /v1/gauges/:id                Permanent delete (only if no readings/invoices)
POST   /v1/gauges/:id/archive        Archive gauge (with reason)
POST   /v1/gauges/:id/restore        Restore archived gauge
GET    /v1/gauges/export             Export gauge list to XLSX

Gauge coefficients (versioned; each new record triggers async consumption recalculation):

GET    /v1/gauges/:id/coefficients   Current + historical versioned coefficients
POST   /v1/gauges/:id/coefficients   Add new versioned coefficient record (triggers async recalculation)

Building total formula (one formula per medium per building; user must confirm any system-suggested change):

GET    /v1/buildings/:id/total-formula    Get building total formula and formula members
PATCH  /v1/buildings/:id/total-formula    Update building total formula (manual adjustment)

Domain Model (ER diagram) & Data Attribute Table ​

No ER diagram in source material. Related entity pages (Confluence, not yet migrated into this repo's Entity DAT catalog):

Data ​

No seed data is required for this feature. Gauges are created by tenant administrators as part of initial system setup. The only automatically created gauge is the buildingTotal virtual gauge, which the system creates when a building total formula is first generated or confirmed.

Test Data ​

N/A — not covered in source material; no Test Data location was specified in the source page.

Logging ​

.info

  • gauge created, updated, archived, restored, deleted
  • physicalMeter created; gauge assigned to physicalMeter (gaugeId, physicalMeterId)
  • coefficient record created (including previous value and new value)
  • buildingTotal formula updated (buildingId, previous formula, new formula)
  • anomaly detected on write (gaugeId, readingId or consumptionId, check type, value)

.debug

  • building total formula auto-generation evaluated (buildingId, result: generated / no-change / alert)
  • sub-gauge parent validation result

Monitoring ​

N/A — not covered in source material.

Caching ​

N/A — not covered in source material.

Backward Compatibility and Migration ​

Key migration mappings from EM2 (GaugeMeter, DataLogger, GaugeSettings):

  • Legacy segments (1, 2, 3, 4, 11, 14…) → EM3 type + direction + tariff on Reading; see the legacy Data Model migration table (out of scope for this pass)
  • Legacy datasets S0A/S0B → source attribute on Reading + query-time best_available view. Priority order (remote > manual > invoice by default) is configurable per gauge via gauge.sourcePriority. The view resolves the highest-priority available source at query time — S0A/S0B as stored tables no longer exist in EM3.
  • Legacy gauge coefficient → volumeCoefficient (versioned, with validFrom = migration date); the legacy manual and remote coefficient tables migrate into its two readingSource histories
  • Legacy calorific values (default_heating_power_types, HeatingPower, GaugeHeatingPower) → defaultCalorificValue and calorificValue
  • Legacy primary_source SMALLINT → gauge.primarySource; enum values 1–28 from GaugeType::SOURCE_* in GaugeType.php; see enum PrimarySource
  • Legacy DataLogger entity (device configuration, OBIS/IEC codes, communication parameters) → Remote Connection Management. EM3 keeps the gauge record clean of communication details; the iecCode (OBIS channel identifier) migrates to the Remote Connection domain, not to the gauge.
  • Legacy gauge_meter.manufacturer → gauge.manufacturer (nullable string, EM2 parity preserved)
  • Legacy subscriber identification → gauge.subscriberId references an Organisation from the Client Management domain; subscriber attributes are read-only on the gauge detail (see Subscriber identification section above)
  • gauge.purpose has no direct EM2 source — since EM2 never distinguished multiple wordings for the same technical combination, migrated gauges are back-filled from gaugePurposeOption using the option flagged isDefaultForCombination = true for the migrated gauge's (kind, medium, direction, outputMode).

The field-by-field mapping of the gauge form from EM2 (which EM2 field becomes what, what is dropped) and the open migration tasks M1–M6 (export of reserved capacities before feature id26 changes their visibility, the EM2 fictitious re-invoicing gauge, usage-type values EM3 lacks, per-channel usage type, gauges without usage type, coefficient split per reading source) are in migrace-em2.md (Czech).

N/A — not addressed in the source Confluence page; migrated as a reference copy without new legal analysis.

Cybersecurity Considerations ​

N/A — not addressed in the source Confluence page; migrated as a reference copy without new security analysis.

Risk Assessment ​

N/A — not addressed in the source Confluence page; migrated as a reference copy without new risk analysis.

Auditing, Reporting & Measurement ​

N/A — not addressed in the source Confluence page; migrated as a reference copy without new analysis.