feat(opcua): support constant datapoints (attributes + one-time telemetry)#1982
Open
palandri wants to merge 2 commits intothingsboard:masterfrom
Open
feat(opcua): support constant datapoints (attributes + one-time telemetry)#1982palandri wants to merge 2 commits intothingsboard:masterfrom
palandri wants to merge 2 commits intothingsboard:masterfrom
Conversation
…emetry) Parse/store type: constant datapoints; send constant attributes on device creation and reconnection; send constant telemetry once per device per process; type inference (bool/int/float/string) with gateway timestamp; respect reportStrategy via TBUtility.convert_key_to_datapoint_key; backward compatible path/identifier behavior.
…ime telemetry) Add fixture config and unit test validating parsing buckets and idempotent constant telemetry send.
Contributor
|
Hi @palandri, thanks for your contribution! We really appreciate it. |
Author
|
Hello @samson0v. Do not worry, there is no hurry. Cheers. |
Contributor
|
Hi @palandri, I am reviewing your PR and have a question about this: Why did you decide to use different strategies for sending? |
samson0v
requested changes
Nov 18, 2025
| new_device = Device(path=device_path, name=device_name, device_profile=device_profile, | ||
| config=device_config, | ||
| converter=converter(device_config, self.__converter_log), | ||
| converter_for_sub=converter(device_config, self.__converter_log) if self.__enable_subscriptions else None, |
Comment on lines
+1109
to
+1110
| has_attributes = False | ||
| has_telemetry = False |
Contributor
There was a problem hiding this comment.
We can check these using ConvertedData telemetry_datapoints_count and attributes_datapoints_count
| converted_data = ConvertedData(device_name=device.name, device_type=device.device_profile) | ||
|
|
||
| # Constant attributes | ||
| for entry in getattr(device, 'constant_attributes', []): |
Contributor
There was a problem hiding this comment.
Is it necessary to check?
| # Constant telemetry (send once per device name per process) | ||
| if device.name not in self.__constant_telemetry_sent_devices: | ||
| telemetry_values = {} | ||
| for entry in getattr(device, 'constant_timeseries', []): |
Contributor
There was a problem hiding this comment.
Is it necessary to check?
| has_telemetry = True | ||
| self.__constant_telemetry_sent_devices.add(device.name) | ||
|
|
||
| if has_attributes or has_telemetry: |
Contributor
|
📢 a little announcement: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(opcua): support constant datapoints (attributes + one-time telemetry)
Summary
Adds first-class support for constant datapoints in the OPC-UA connector:
Motivation
Previously, OPC-UA mapping handled datapoints backed by NodeId or path expressions but ignored constants. This feature enables:
Implementation Details
python.Device.load_values().python.OpcUaConnector.__send_constants_for_device().python.OpcUaConnector._create_new_devices().thingsboard_gateway/connectors/opcua/opcua_connector.py.python.TBUtility.convert_key_to_datapoint_key().python.OpcUaConnector.__guess_type_and_cast().python.TelemetryEntry.__init__().Behavior
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
Backward Compatibility
Tests / Verification
tests/unit/connectors/opcua/test_constants.pytests/unit/connectors/opcua/data/constants/opcua_config_constants.jsontests/blackbox/data/opcua/configs/uplink_configs/constants_config.jsonDocumentation Impact
Touched Code
thingsboard_gateway/connectors/opcua/device.pypython.Device.load_values()python.OpcUaConnector.__send_constants_for_device()python.OpcUaConnector._create_new_devices()python.OpcUaConnector.__guess_type_and_cast()python.TBUtility.convert_key_to_datapoint_key()python.TelemetryEntry.__init__()tests/unit/connectors/opcua/test_constants.pytests/unit/connectors/opcua/data/constants/opcua_config_constants.jsontests/blackbox/data/opcua/configs/uplink_configs/constants_config.jsonChecklist