Appearance
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).buildingIdassignment (physical/administrative ownership of a gauge) is independent frombuildingTotalformula 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/outputModecombinations — 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
buildingIdis 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
buildingIdreassignment — they resolve their building transitively via the gauge's currentbuildingId, so a move is reflected retroactively in historical views
- when
gauge.kindandgauge.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 forkind = virtual - KVP gauges (
medium = kvp) have nooutputModeregardless of kind; the field is hidden in the UI and rejected by the API whenmedium = kvp - A channel whose
gauge.readingMethod = remotedoes not accept a manual reading: the manual entry form is hidden for it and the API rejects a reading withsource = manual.readingMethod = manualorbothaccepts 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/subtractis created or itsoutputModechanges:- 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
Loom Link
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:
| Kind | Physical device | outputMode | In gauge list | Example |
|---|---|---|---|---|
standard | Yes | Required (except medium = kvp) | Yes | Main electricity meter, gas meter |
subGauge | Yes (sub-circuit) | Required (except medium = kvp) | Yes | Floor-level electricity sub-meter |
virtual / computed | No | None | Yes | FVE self-consumption formula |
virtual / manualInput | No | None | Yes | Cost allocation from landlord invoice |
virtual / buildingTotal | No | None | No (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.
| Medium | UI option (EN / CZ) | direction stored | Typical reading type | outputMode applicable |
|---|---|---|---|---|
electricity | Consumed from grid / Odebráno ze sítě | import | energy, power | Yes |
electricity | Returned to grid / Dodáno do sítě | export | energy, power | Yes |
electricity | Production (FVE, CHP, …) / Výroba | export | energy, power | Yes — typically reportOnly |
electricity | Battery charging / Nabíjení baterie | import | energy, power | Yes — typically reportOnly |
electricity | Battery discharging / Provoz na baterii | export | energy, power | Yes — typically reportOnly |
gas | Consumption / Spotřeba | import | energy, volume | Yes |
water | Consumption / Spotřeba | import | volume | Yes |
heat | Consumption / Spotřeba | import | energy | Yes |
heat | Production (KGJ, …) / Výroba tepla | export | energy | Yes — typically reportOnly |
fuel | Consumption / Spotřeba | import | volume | Yes |
phm | Consumption / Spotřeba | import | volume | Yes |
kvp | Temperature / Teplota | null | temperature | No — always null |
kvp | Humidity / Vlhkost | null | humidity | No — always null |
kvp | CO₂ concentration / Koncentrace CO₂ | null | co2 | No — always null |
- for
kvpthe UI does not show a direction selector (always stored asnull) - gas meters can produce readings of type
energy(kWh, if the device converts inline) orvolume(m³, converted to energy later using the medium's calorific value) — the medium isgasin 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 = standardorsubGaugewithmedium ≠ kvp; virtual gauges never haveoutputModeregardless 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 tobuildingTotalformula 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 thegaugePurposeOptionmechanism 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.purposestores a reference to the specificgaugePurposeOptionthe 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
gaugePurposeOptionrow - adding an entirely new combination (a
direction/outputModepairing that isn't already valid) still requires developer involvement — tied to enum values andbuildingTotalformula logic
Worked example — two wordings, one configuration
| translationKey | kind | medium | targetDirection | targetOutputMode | purposeCategory | isDefaultForCombination |
|---|---|---|---|---|---|---|
gauge.purposeOption.consumedFromGrid | standard | electricity | import | include | consumedFromGrid | true |
gauge.purposeOption.consumedFromGridAlt | standard | electricity | import | include | consumedFromGrid | false |
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:
| Kind | What changes |
|---|---|
standard | Medium 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. |
subGauge | Parent 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. |
virtual | Subtype (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.
| Medium | UI label | Format | Required |
|---|---|---|---|
electricity | EAN | Exactly 18 digits | Yes — for kind = standard and direction = import; a 4Q meter's export channel carries its own EAN |
gas | EIC | 16 alphanumeric characters | Recommended |
water, heat, fuel, phm | Identifikační číslo OM | Max 50 characters, free string | No |
kvp, virtual | — | Hidden in UI | No |
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 withsource = 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:
- The user creates the first channel (e.g. import) as a regular gauge — no mention of physical meter grouping.
- On the gauge detail page, a "+ Add channel" button is visible. The user clicks it.
- A pre-filled form opens:
mediumis locked (inherited from the first channel),buildingIdis pre-set. The user fills in only the channel-specific fields: direction,supplyPointId, unit, name. - On save, the backend transparently creates or reuses the
physicalMeterrecord and assignsphysicalMeterIdto 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:
| outputMode | direction | Effect on building total |
|---|---|---|
include | import | Added (+) |
include | export | Subtracted (−) |
subtract | import | Subtracted (−) |
subtract | export | Added (+) |
exclude | any | Ignored |
reportOnly | any | Ignored 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 has | Classification | Result |
|---|---|---|
| ≥1 INCLUDE standard gauge + REPORT_ONLY sub-gauges in the same building | Known valid pattern — downstream measurement | buildingTotal auto-generated from INCLUDE gauges. No alert. |
| INCLUDE sub-gauge(s), no INCLUDE standard gauge | Known valid pattern — third-party building | buildingTotal auto-generated from INCLUDE sub-gauges. No alert. |
| Sub-gauge in Building B has its parent gauge in Building A | Cross-building dependency | Alert: "Building A total may be incomplete — sub-gauge [name] in Building B draws from [MG1] in Building A." |
| No INCLUDE gauge at all | Incomplete configuration | Alert: "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
volumeCoefficientrecord with avalidFromdate - 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 flaggedgauge.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
clientTolerancerecord 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:
- Negative value (controlled by
allowsNegative) - Value lower than previous cumulative reading (suspected meter replacement or data error)
- Stagnation — same value repeated across N consecutive 15-minute intervals
- Null / empty value from remote device
- 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 = nullalways — the UI does not show a direction selector for KVP gaugesoutputMode = nullalways — KVP gauges are never part of any building total, regardless ofkind. The field is hidden in the UI and rejected by the API whenmedium = kvp.- their readings have
type = temperature / humidity / co2andtariff = 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 field | Source attribute | Notes |
|---|---|---|
| Name | organisation.name | Always shown |
| IČO | organisation.ico | Shown when non-null |
| DIČ | organisation.dic | Shown when non-null |
| Legal form | organisation.legalForm | Shown when non-null |
| Registered address | organisation.registeredStreet + registeredCity + registeredZip | Shown when any address field is non-null |
| EnMS contact | organisation.contactEnmsUserId | Resolved to user display name + email; shown when non-null |
| Billing contact | organisation.contactBillingUserId | Resolved to user display name + email; shown when non-null |
| Technical contact | organisation.contactTechnicalUserId | Resolved to user display name + email; shown when non-null |
| Regulatory contact | organisation.contactRegulatoryUserId | Resolved 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, andarchiveReason); thearchiveReasonis inherited from the building archival reason. - Archiving a gauge sets
gauge.isArchived= true, recordsarchivedAt, and requires anarchiveReason. - When a gauge is archived as part of a building cascade, its
archiveReasonis set to the same value as the building'sarchiveReason(direct mapping). All fourBuildingArchiveReasonvalues (saleDemolition,transfer,outOfScope,other) have a matching value inGaugeArchiveReason. No conversion logic is needed. - Archived gauges are excluded from active lists by default; the
showArchivedInReportsflag 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 Acceptedand 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:
- Checks whether the referenced gauge already has a
physicalMeterId. - If not — creates a new
physicalMeterrecord, assigns its UUID to the existing gauge (PATCH gauge SET physicalMeterId) and to the new gauge being created. - If yes — assigns the existing
physicalMeterIdto 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.parentGaugeIdexists and haskind = 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 XLSXGauge 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):
- gauge — physical and virtual measurement points, lifecycle, coefficients. The 23 Sep design round added the supply type, measurement type, reading method, placement and identification fields and the whole energy-purchase (PXE) set;
readingMode,allowsManualReadingandlabelOverrideretire; the meter-levelusageTypeis replaced (5 Oct) by the channel allocation gaugeUsageAllocation / gaugeUsageAllocationItem over the platform catalogue usageType. See entities/gauge. - gaugeSourceSetting — unit, reading frequency and coefficient per reading source (manual, remote), versioned; replaces the single unit and coefficient on the gauge. See entities/gaugeSourceSetting
- gaugeFormula and gaugeFormulaMember — versioned formula of a computed virtual gauge and its signed members. See entities/gaugeFormula and entities/gaugeFormulaMember.
- gaugeMonthlyValue — twelve monthly figures per supply point: agreed gas consumption (whose sum is the read-only annual consumption) and monthly reserved capacities. See entities/gaugeMonthlyValue
- New enums: GaugeSupplyType, GaugeMeasurementType, GaugeReadingMethod, GaugeBillingType, GaugeDepositType, GaugeContractType, GaugeDeviationResponsibility
- volumeCoefficient — versioned meter-correction coefficients per gauge, one history per reading source
- calorificValue — fuel-to-energy factors at gauge or client scope; see Calorific Value Management
- gaugePurposeOption — superadmin-editable "word choice" options presented at gauge creation
- gaugeSerialHistory — versioned history of serial number changes per gauge
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)
buildingTotalformula 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 →
sourceattribute on Reading + query-timebest_availableview. Priority order (remote > manual > invoice by default) is configurable per gauge viagauge.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, withvalidFrom= migration date); the legacy manual and remote coefficient tables migrate into its tworeadingSourcehistories - Legacy calorific values (
default_heating_power_types,HeatingPower,GaugeHeatingPower) →defaultCalorificValueandcalorificValue - Legacy
primary_sourceSMALLINT →gauge.primarySource; enum values 1–28 fromGaugeType::SOURCE_*inGaugeType.php; see enumPrimarySource - Legacy
DataLoggerentity (device configuration, OBIS/IEC codes, communication parameters) → Remote Connection Management. EM3 keeps the gauge record clean of communication details; theiecCode(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.subscriberIdreferences an Organisation from the Client Management domain; subscriber attributes are read-only on the gauge detail (see Subscriber identification section above) gauge.purposehas no direct EM2 source — since EM2 never distinguished multiple wordings for the same technical combination, migrated gauges are back-filled fromgaugePurposeOptionusing the option flaggedisDefaultForCombination = truefor 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).
Legal Context
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.