thingsboard-gateway
thingsboard-gateway copied to clipboard
feat(opcua): support constant datapoints (attributes + one-time telemetry)
feat(opcua): support constant datapoints (attributes + one-time telemetry)
Summary
Adds first-class support for constant datapoints in the OPC-UA connector:
- Attributes with type: "constant" are sent when a device is created and on every reconnection.
- Telemetry with type: "constant" is sent once per device per gateway process (idempotent).
- Values are auto-cast (bool/int/float) when possible; otherwise kept as string.
- Telemetry is timestamped with the gateway time.
- Report strategy is respected at datapoint level via TBUtility.
Motivation
Previously, OPC-UA mapping handled datapoints backed by NodeId or path expressions but ignored constants. This feature enables:
- Populating device attributes with fixed metadata (e.g., Customer, Location).
- Emitting one-time constant telemetry (e.g., initial setup values) without requiring OPC-UA nodes.
Implementation Details
- Parsing/modeling on device side:
- Added in-memory collections (not polled/subscribed from OPC-UA):
- constant_attributes and constant_timeseries, collected during device config parsing in
python.Device.load_values().
- constant_attributes and constant_timeseries, collected during device config parsing in
- Added in-memory collections (not polled/subscribed from OPC-UA):
- Sending pipeline on connector:
- New routine to emit constants:
python.OpcUaConnector.__send_constants_for_device(). - Invoked right after device creation in
python.OpcUaConnector._create_new_devices(). - Idempotence for constant telemetry via in-memory set created in
thingsboard_gateway/connectors/opcua/opcua_connector.py. - Key building preserves report strategy:
python.TBUtility.convert_key_to_datapoint_key(). - Auto type inference for constant values:
python.OpcUaConnector.__guess_type_and_cast(). - Telemetry timestamp uses gateway time by default:
python.TelemetryEntry.__init__().
- New routine to emit constants:
Behavior
- Attributes (type: "constant")
- Sent on device creation and every reconnection (devices are recreated on reconnect).
- Telemetry (type: "constant")
- Sent exactly once per device per process (not re-sent on reconnects).
Configuration Example (new mapping format)
{
"mapping": [
{
"deviceNodePattern": "Root\\.Objects\\.Device1",
"deviceInfo": {
"deviceNameExpressionSource": "path",
"deviceNameExpression": "Device ${Root\\.Objects\\.Device1\\.serialNumber}",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [
{ "key": "Customer", "type": "constant", "value": "ACME Corp" }
],
"timeseries": [
{ "key": "InitialBatchSize", "type": "constant", "value": "12" }
]
}
]
}
Casting Rules
- "true"/"false" (case-insensitive) → boolean
- -123 → integer
- 45.67 → float
- Anything else → string
Backward Compatibility
- No breaking changes. Existing path/identifier-based datapoints remain unchanged.
- The backward-compatibility adapter continues to set type: "constant" where applicable; the new pipeline now handles those entries.
Tests / Verification
- Unit tests (added in this PR):
tests/unit/connectors/opcua/test_constants.py- Validates parsing buckets for constants and idempotent behavior of one-time constant telemetry.
- Fixture config:
tests/unit/connectors/opcua/data/constants/opcua_config_constants.json
- Blackbox (optional, requires TB test environment):
- Example configuration to validate runtime behavior:
tests/blackbox/data/opcua/configs/uplink_configs/constants_config.json
- Integrates with existing OPC-UA blackbox harness. Not executed as part of this PR by default.
- Example configuration to validate runtime behavior:
Documentation Impact
- Update OPC-UA connector mapping docs to include:
- Support for type: "constant" in attributes and timeseries.
- Semantics (attributes on create/reconnect, telemetry one-time).
- Casting rules and gateway timestamp.
- Example mapping similar to the snippet above.
Touched Code
- Device parsing and storage:
thingsboard_gateway/connectors/opcua/device.pypython.Device.load_values()
- Connector logic:
python.OpcUaConnector.__send_constants_for_device()python.OpcUaConnector._create_new_devices()python.OpcUaConnector.__guess_type_and_cast()
- Utilities and entities (used as-is):
python.TBUtility.convert_key_to_datapoint_key()python.TelemetryEntry.__init__()
- Tests:
tests/unit/connectors/opcua/test_constants.pytests/unit/connectors/opcua/data/constants/opcua_config_constants.jsontests/blackbox/data/opcua/configs/uplink_configs/constants_config.json
Checklist
- [x] Parse/store constant datapoints (no OPC-UA read required)
- [x] Send constant attributes on create/reconnect
- [x] Send constant telemetry once per device per process
- [x] Type inference and gateway timestamp
- [x] Datapoint-level report strategy
- [x] Unit tests
- [ ] Blackbox validation on TB test environment
- [ ] Documentation and CHANGELOG updates
Hi @palandri, thanks for your contribution! We really appreciate it. I will take a look at your PR soon.
Hello @samson0v. Do not worry, there is no hurry. I'm looking forward your opinion on this.
Cheers.
Hi @palandri, I am reviewing your PR and have a question about this:
Attributes (type: "constant")
Sent on device creation and every reconnection (devices are recreated on reconnect).
Telemetry (type: "constant")
Sent exactly once per device per process (not re-sent on reconnects).
Why did you decide to use different strategies for sending?