This is a protocol adapter service for the OpenEnergyTwin Platform.
This service bridges various industrial and smart meter protocols (IEC 60870-5-104, COSEM/XML) to the OpenEnergyTwin MQTT-based messaging infrastructure. It also implements the BDEW Universal Order Process (Universalbestellprozess) for sending control commands to intelligent meter systems in Germany.
For deployment, this service requires:
- A running MQTT broker for communication with other services
- Configuration for the adapters to connect (IEC 104 servers, COSEM endpoints, BDEW API servers)
| Variable | Description | Required | Default |
|---|---|---|---|
RUST_LOG |
Logging level (e.g., info, debug, warn) |
No | info |
Configuration is done via a config.toml file. The service reads this file at startup.
# MQTT broker connection
mqtt_url = "mqtt://localhost:1883/?client_id=protocol_adapter"
# List of adapter configurations
[[adapters]]
# Adapter type and specific configuration...Connects to IEC 60870-5-104 servers to collect measurement data.
[[adapters]]
type = "IEC104Client"
server_url = "192.168.1.100:2404"
# Define measurement points to monitor
measurement_points = [
{ common_address = 1, information_object_address = 1001, type = "ActivePower", terminal = "terminal_42" },
{ common_address = 1, information_object_address = 1002, type = "ReactivePower", terminal = "terminal_42" },
{ common_address = 1, information_object_address = 2001, type = "VoltageMagnitude", topological_node = "node_123" },
]Configuration Fields:
| Field | Description | Required |
|---|---|---|
type |
Must be "IEC104Client" |
Yes |
server_url |
IEC 104 server address with port (e.g., "192.168.1.100:2404") |
Yes |
measurement_points |
Array of measurement point definitions | Yes |
Measurement Point Fields:
| Field | Description | Required |
|---|---|---|
common_address |
IEC 104 common address (CA) of the measurement point | Yes |
information_object_address |
Information object address (IOA) | Yes |
type |
Measurement type: "ActivePower", "ReactivePower", or "VoltageMagnitude" |
Yes |
terminal |
Terminal ID (required for ActivePower and ReactivePower) | Conditional |
topological_node |
Topological node ID (required for VoltageMagnitude) | Conditional |
Exposes an HTTP server that receives COSEM/XML data from smart meters and forwards it as measurement messages.
[[adapters]]
type = "COSEMServer"
host = "0.0.0.0"
port = 3000
# Define smart meter mappings
smart_meters = [
{ device_id = "42", terminal = "terminal_42", topological_node = "node_123" },
{ device_id = "43", terminal = "terminal_43", topological_node = "node_124" },
]Configuration Fields:
| Field | Description | Required |
|---|---|---|
type |
Must be "COSEMServer" |
Yes |
host |
Host address to bind the HTTP server | Yes |
port |
Port to bind the HTTP server | Yes |
smart_meters |
Array of smart meter definitions | Yes |
Smart Meter Fields:
| Field | Description | Required |
|---|---|---|
device_id |
Unique device identifier (used in HTTP query parameter) | Yes |
terminal |
Terminal ID for the smart meter | Yes |
topological_node |
Topological node ID for the smart meter | Yes |
HTTP Endpoint:
- Path:
/cosem/ldevs?devID={device_id} - Method: POST
- Content-Type: application/xml
- Body: COSEM ProgObject XML
The server expects COSEM data in XML format containing OBIS codes for:
1-0:16.7.0*255- Active power total1-0:36.7.0*255- Active power L11-0:56.7.0*255- Active power L21-0:76.7.0*255- Active power L3
Implements the BDEW Universal Order Process for sending control commands to smart meter systems (iMS) according to §14a EnWG.
[[adapters]]
type = "BDEWUniversalbestellprozessClient"
server_url = "https://api.bdew.example.com"
webhook_host = "0.0.0.0"
webhook_port = 5000
# Define location mappings
locations = [
{ location_id = "C1234848431", energy_consumer = "_caf0f2d6-3cf5-4605-8911-f36ed78bf9fb" },
]Configuration Fields:
| Field | Description | Required |
|---|---|---|
type |
Must be "BDEWUniversalbestellprozessClient" |
Yes |
server_url |
Base URL of the BDEW API server | Yes |
webhook_host |
Host address for the webhook callback server | Yes |
webhook_port |
Port for the webhook callback server | Yes |
locations |
Array of location mappings | Yes |
Location Fields:
| Field | Description | Required |
|---|---|---|
location_id |
BDEW location ID (SR-ID pattern C[A-Z\d]{9}\d or Nelo-ID pattern E[A-Z\d]{9}\d) |
Yes |
energy_consumer |
Internal equipment ID that maps to this location | Yes |
Webhook Endpoints:
The service exposes the following webhook endpoints to receive responses from the BDEW API:
| Endpoint | Method | Description |
|---|---|---|
/[Post]/steuerbefehl/vorlaeufigePositiveAntwort/ |
POST | Preliminary positive response |
/[Post]/steuerbefehl/vorlaeufigeNegativeAntwort/ |
POST | Preliminary negative response |
/[Post]/steuerbefehl/positiveAntwort/ |
POST | Final positive response |
/[Post]/steuerbefehl/negativeAntwort/ |
POST | Final negative response |
/[Post]/steuerbefehl/informationAnweisung/ |
POST | Unknown status information |
Command Flow:
The BDEW Universal Order Process follows this asynchronous pattern:
- Platform sends a command via MQTT (
Controltopic) in CIM JSON-LD format withcim:ActivePowerLimit - Protocol adapter parses the command and sends it to the BDEW API
- BDEW API responds with
202 Acceptedand a transaction ID - MSB (Messstellenbetreiber) sends responses via webhooks
- Command completion is tracked and logged
CIM Command Message Format:
{
"@graph": [
{
"@id": "_977b3685-8e3b-4b1a-aa09-05941f5488f8",
"@type": "cim:OperationalLimitType",
"cim:IdentifiedObject.name": "§14a EnWG Congestion Limit",
"cim:OperationalLimitType.acceptableDuration": 3600,
"cim:OperationalLimitType.limitType": "cim:LimitTypeKind.tatl",
"cim:OperationalLimitType.direction": "cim:OperationalLimitDirectionKind.high"
},
{
"@id": "_a82c88f4-0b73-40dd-b5de-b6e014c480b2",
"@type": "cim:OperationalLimitSet",
"cim:IdentifiedObject.name": "§14a Limit Set",
"cim:OperationalLimitSet.Equipment": {
"@id": "_caf0f2d6-3cf5-4605-8911-f36ed78bf9fb"
}
},
{
"@id": "_7fa766ef-ee79-4b2d-98cf-33f6a71acd0c",
"@type": "cim:ActivePowerLimit",
"cim:IdentifiedObject.name": "Consumption Limit 4.2 kW",
"cim:ActivePowerLimit.value": 4.2,
"cim:OperationalLimit.OperationalLimitSet": {
"@id": "_a82c88f4-0b73-40dd-b5de-b6e014c480b2"
},
"cim:OperationalLimit.OperationalLimitType": {
"@id": "_977b3685-8e3b-4b1a-aa09-05941f5488f8"
}
}
]
}| Topic | QoS | Description |
|---|---|---|
Control |
1 | Receives control commands in CIM JSON-LD format |
| Topic | QoS | Description |
|---|---|---|
Measurement.Raw |
1 | Publishes measurement data in CIM format |
The service publishes measurement data in CIM format:
Active Power:
{
"@type": "cim:SvPowerFlow",
"cim:SvPowerFlow.Terminal": "terminal_42",
"cim:SvPowerFlow.p": 4.2,
"cim:SvPowerFlow.phase": 1
}Reactive Power:
{
"@type": "cim:SvPowerFlow",
"cim:SvPowerFlow.Terminal": "terminal_42",
"cim:SvPowerFlow.q": 0.5,
"cim:SvPowerFlow.phase": null
}Voltage Magnitude:
{
"@type": "cim:SvVoltage",
"cim:SvVoltage.TopologicalNode": "node_123",
"cim:SvVoltage.v": 1.0
}