Metrics Schema
The metrics schema defines all telemetry metrics as JSON. A generator transforms these definitions into strongly-typed C# code, Elasticsearch mappings, and reference documentation.
Overview
Metric definitions live in schemas/metrics/. Each domain (battery, energy, etc.) has its own registry file in schemas/metrics/registries/. The root metrics.json file defines shared enums (units, profiles) and points to the registries.
The generator reads these definitions and produces:
- C# classes in
src/Voltimax.Iot.Contracts/Metrics/Generated/ - Elasticsearch mappings in the same output directory
- Documentation in
docs/reference/metrics/
Quick Start
dotnet run --project tools/Voltimax.Iot.Tools -- generateThis regenerates all output files and removes obsolete generated files automatically.
Registry Format
Each registry file defines a domain and its metrics. The schema for registry files is in schemas/metrics/schemas/. A minimal example:
{
"id": "energy",
"name": "Energy",
"description": "Electrical power and energy measurements",
"metrics": [
{
"name": "ActivePower",
"key": "active_power",
"unit": "Watt",
"description": "Active power",
"scopes": ["sum", "l1", "l2", "l3"],
"profile": "Basic"
}
]
}Every metric requires name (PascalCase, becomes the C# property), key (snake_case, becomes the JSON/Elasticsearch field), unit (from the Unit enum in metrics.json), and description. Optional fields: scopes (expands into multi-variant metrics), profile (Basic/Standard/Advanced), and counter (monotonically increasing).
Scopes
The scopes array expands a single definition into multiple metrics. A metric with "scopes": ["sum", "l1", "l2", "l3"] generates four metrics: ActivePower_Sum (ID active_power#sum), ActivePower_L1, ActivePower_L2, ActivePower_L3. Without scopes, a single base metric is generated.
Common scope values: sum (aggregated), l1/l2/l3 (per-phase), n (neutral), l1_l2/l2_l3/l3_l1 (line-to-line), t1/t2 (tariff periods). Choose scopes that match the physical measurement - for example, use sum + per-phase for power, but only line-to-line for line voltages.
Metric ID Format
Metrics use a structured ID: domain.identifier#scope
energy.active_power#sum- domainenergy, identifieractive_power, scopesumbattery.soc- no scope (base metric)
The # delimiter enables unambiguous parsing and consistent filtering by domain, identifier, or scope.
Adding New Metrics
1. Define the metric
Add to an existing registry in schemas/metrics/registries/ or create a new file for a new domain:
{
"name": "NewMetric",
"key": "new_metric",
"unit": "Watt",
"description": "Clear description of what this measures",
"profile": "Standard"
}2. Regenerate and verify
dotnet run --project tools/Voltimax.Iot.Tools -- generate
dotnet buildBest Practices
Naming
name: PascalCase (ActivePower,VoltageLineToNeutral)key: snake_case (active_power,voltage_ln)- Scopes: lowercase (
l1,sum,t1)
Descriptions
Write descriptions that explain what the metric measures, not how:
- Good:
"Active power (real power consumed or generated)" - Too vague:
"Power measurement" - Too technical:
"P = V × I × cos(φ)"
Related Documentation
- Generator Internals - expansion process and generator architecture
- Data Model - metric keys, units, asset types, and profiles
- Metrics Reference - generated metric specifications
- Asset Generator - Modbus device generation
- Troubleshooting - common issues and solutions