Google Cloud Pub/Sub Integration
Pub/Sub Integration connects ThingsBoard to Google Cloud Pub/Sub as a subscriber. It receives messages from a pull subscription, and an uplink data converter transforms each message into device or asset data, including telemetry and attributes.
Use Pub/Sub Integration when:
- Devices or backend services publish data to a Pub/Sub topic.
- One subscription carries data for many devices: the converter sets the target device for each message.
- Message attributes identify the device or describe the data: the converter receives them as metadata.
Architecture
Section titled “Architecture”- Receive data. A publisher sends a message to a topic. Pub/Sub delivers it to the pull subscription, and the integration receives it over a streaming pull connection.
- Decode the payload. The uplink converter identifies the device or asset and extracts attributes and telemetry.
- Process the message. ThingsBoard processes the converted data through the Rule Engine. If Allow create devices or assets is enabled, ThingsBoard can create an entity that does not exist yet.
- Acknowledge the message. The integration acknowledges each message after processing, including a message that fails conversion. Pub/Sub does not deliver it again.
Prerequisites
Section titled “Prerequisites”- Access to ThingsBoard Cloud with integrations enabled for your tenant.
- Permission to create integrations and data converters, and to edit rule chains to publish data back to Pub/Sub.
- A Google Cloud project where you can enable APIs and create Pub/Sub topics, subscriptions, and service accounts.
Set Up Google Cloud Pub/Sub
Section titled “Set Up Google Cloud Pub/Sub”Enable the API, create a topic with a pull subscription, and create a service account key in the Google Cloud console.
Enable the Cloud Pub/Sub API
Section titled “Enable the Cloud Pub/Sub API”- Open your project, click Search in the top toolbar, and search for Cloud Pub/Sub API.
- Select Cloud Pub/Sub API in the results.
- Click Enable. If the API is already enabled, skip this step.
- Check that the API page shows Status: Enabled.
Create a Topic and a Subscription
Section titled “Create a Topic and a Subscription”- Search for Pub/Sub and open it.
- On the Topics page, click Create topic.
- Enter a Topic ID, for example
tb-uplink. Keep Add a default subscription selected: it creates the pull subscriptiontb-uplink-sub. - Click Create.
- On the topic’s Subscriptions tab, open
tb-uplink-suband check that its state is Active and its delivery type is Pull.
Create a Service Account Key
Section titled “Create a Service Account Key”The integration authenticates with a service account key. To receive messages, the account needs the Pub/Sub Subscriber role.
- Search for Service Accounts and open it.
- Click Create service account.
- Enter a name, for example
tb-pubsub, and click Create and continue. - In Permissions, select the Pub/Sub Subscriber role, then click Continue and Done.
- Click the email of the new service account.
- On the Keys tab, click Add key ⇾ Create new key.
- Select JSON and click Create. The browser downloads the key file.
- Check that the key appears as Active on the Keys tab.
You now have the values for the integration:
| Value | Example | Source |
|---|---|---|
| Project ID | tb-pubsub-integration |
Project selector or the Project info card on the dashboard |
| Subscription ID | tb-uplink-sub |
Last segment of the subscription name |
| Service Account key file | tb-pubsub-integration-1a2b3c4d5e6f.json |
Downloaded key file |
Set Up Pub/Sub Integration
Section titled “Set Up Pub/Sub Integration”Create the uplink converter, assign it to a new Pub/Sub integration, and publish a test message.
Create the Uplink Data Converter
Section titled “Create the Uplink Data Converter”The uplink converter reads the message data and maps it to ThingsBoard entities and data. Pub/Sub Integration uses the Generic converter: you write the decoder in TBEL or JavaScript. For converter types and the full decoder reference, see Uplink data converter.
-
Go to Integrations ⇾ Data converters and click + Add data converter ⇾ Create new converter.
-
Converter type — keep Uplink.
-
Integration type — select Pub/Sub.
-
Name — enter a name, for example
Pub/Sub Uplink Converter. -
Main decoding configuration — the editor opens with the default decoder below. Keep it for JSON messages: it takes the device name and type from
deviceNameanddeviceType, storesserialNumberas an attribute, and stores the other fields as telemetry. The timestamp comes fromts(Unix milliseconds) ortimestamp(ISO 8601); without them, the decoder uses the server time. The editor opens in TBEL; use the TBEL / JS toggle to switch to JavaScript.// Decode an uplink message from a buffer// payload - array of bytes// metadata - key/value object/** Decoder **/// decode payload to JSONvar data = decodeToJson(payload);// --- Device name and type ---var deviceName = data.deviceName != null ? data.deviceName : 'Unknown Device';var deviceType = data.deviceType != null ? data.deviceType : 'default';// var customerName = 'Customer C';// var groupName = 'thermostat devices';// use assetName and assetType instead of deviceName and deviceType// to automatically create assets instead of devices.// var assetName = 'Asset A';// var assetType = 'building';// --- Timestamp parsing ---var timestamp = -1;if (data.ts != null) {timestamp = data.ts;} else if (data.timestamp != null) {timestamp = new Date(data.timestamp).getTime();}if (timestamp == -1) {timestamp = Date.now();}// --- Telemetry and attributes ---var telemetry = {};// Keys from the payload that should be stored as attributes instead of telemetryvar attributeKeyList = ["serialNumber"];// Add the payload keys that match the attribute key list to attributesvar attributes = {};for (var i = 0; i < attributeKeyList.size(); i++) {var attributeKey = attributeKeyList[i];if (data[attributeKey] != null) {attributes[attributeKey] = data[attributeKey];}}// Keys to exclude from telemetry (device info, attributes and non-telemetry fields)var excludeFromTelemetryList = ["deviceName", "deviceType", "ts", "timestamp"];excludeFromTelemetryList.addAll(attributeKeyList);// Parse all remaining JSON fields as telemetrytelemetry.putAll(toFlatMap(data, excludeFromTelemetryList, true));// Result object with device attributes/telemetry datavar result = {deviceName: deviceName,deviceType: deviceType,// customerName: customerName,// groupName: groupName,// assetName: assetName,// assetType: assetType,};// Add attributes to the result only if at least one matching key was foundif (attributes.size() > 0) {result.put("attributes", attributes);}result.put("telemetry", {ts: timestamp,values: telemetry});/** Helper functions 'decodeToString', 'decodeToJson' and 'toFlatMap' are already built-in **/return result;// Decode an uplink message from a buffer// payload - array of bytes// metadata - key/value object/** Decoder **/// decode payload to JSONvar data = decodeToJson(payload);// --- Device name and type ---var deviceName = data.deviceName != null ? data.deviceName : 'Unknown Device';var deviceType = data.deviceType != null ? data.deviceType : 'default';// var customerName = 'Customer C';// var groupName = 'thermostat devices';// use assetName and assetType instead of deviceName and deviceType// to automatically create assets instead of devices.// var assetName = 'Asset A';// var assetType = 'building';// --- Timestamp parsing ---var timestamp = -1;if (data.ts != null) {timestamp = data.ts;} else if (data.timestamp != null) {timestamp = new Date(data.timestamp).getTime();}if (timestamp == -1) {timestamp = Date.now();}// --- Telemetry and attributes ---var telemetry = {};// Keys from the payload that should be stored as attributes instead of telemetryvar attributeKeyList = ["serialNumber"];// Add the payload keys that match the attribute key list to attributesvar attributes = {};for (var i = 0; i < attributeKeyList.length; i++) {var attributeKey = attributeKeyList[i];if (data[attributeKey] != null) {attributes[attributeKey] = data[attributeKey];}}// Keys to exclude from telemetry (device info, attributes and non-telemetry fields)var excludeFromTelemetryList = ["deviceName", "deviceType", "ts", "timestamp"].concat(attributeKeyList);// Parse all remaining JSON fields as telemetrytoFlatMap(data, telemetry, excludeFromTelemetryList);// Result object with device attributes/telemetry datavar result = {deviceName: deviceName,deviceType: deviceType,// customerName: customerName,// groupName: groupName,// assetName: assetName,// assetType: assetType,};// Add attributes to the result only if at least one matching key was foundif (Object.keys(attributes).length > 0) {result.attributes = attributes;}result.telemetry = {ts: timestamp,values: telemetry};/** Helper functions **/function decodeToString(payload) {return String.fromCharCode.apply(String, payload);}function decodeToJson(payload) {var str = decodeToString(payload);return JSON.parse(str);}function toFlatMap(obj, result, excludeList, prefix) {prefix = prefix || '';for (var key in obj) {if (excludeList.indexOf(key) !== -1) continue;var value = obj[key];var fullKey = prefix ? prefix + '.' + key : key;if (typeof value === 'object' && value !== null && !Array.isArray(value)) {toFlatMap(value, result, excludeList, fullKey);} else {result[fullKey] = value;}}}return result;To adapt:
- Device name in a message attribute — read it from
metadata, for examplevar deviceName = metadata.deviceId;. - Device name in another field — replace
data.deviceName, for example withdata.id. - Other attributes — add keys to
attributeKeyList, for example"firmwareVersion". - Assets instead of devices — uncomment
assetNameandassetType, and use them in the result instead ofdeviceNameanddeviceType. - Customer or group — uncomment
customerNameorgroupNameand add it to the result.
- Device name in a message attribute — read it from
-
Click Test decoder function and check the result (see Test the Decoder).
-
Click Add.
Decoder Inputs
The decoder signature is function decoder(payload, metadata): object | object[].
| Variable | Type | Content |
|---|---|---|
payload |
byte array | The message data. |
metadata |
{[key: string]: string} |
integrationName, pubSubMsgId (the message ID assigned by Pub/Sub), every message attribute as a key, and the key–value pairs from the integration’s Metadata field. A message attribute overrides a Metadata pair with the same key. |
Built-in helpers:
| Function | Result |
|---|---|
decodeToJson(payload) |
Parses the bytes as JSON and returns an object. |
decodeToString(payload) |
Converts the bytes to a UTF-8 string. |
toFlatMap(obj, excludeKeys, flatNested) |
TBEL only. Returns a flat key–value map of obj, skipping excludeKeys. With flatNested set to true, nested objects become dot-separated keys. The JavaScript template defines its own toFlatMap(obj, result, excludeList), which writes into result. |
For field renaming, nested values, and non-JSON payloads, see Common scripting patterns.
Test the Decoder
Click Test decoder function below the editor. The dialog opens with Payload content type set to JSON and this sample payload:
{ "deviceName": "Thermometer", "deviceType": "thermostat", "serialNumber": "SN-12345", "temperature": 25.5}Click Test. The Output panel shows:
{ "deviceName": "Thermometer", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-12345" }, "telemetry": { "ts": 1791807972006, "values": { "temperature": 25.5 } }}ts is the current time, because the payload has neither ts nor timestamp. Check that deviceName is set and telemetry.values holds the expected keys. If the output is empty or shows an error, fix the decoder and test again.
Create the Integration
Section titled “Create the Integration”The integration has no downlink, so the wizard skips the downlink converter step.
- Go to Integrations ⇾ Integrations and click + Add integration.
- Basic settings:
- Integration type — select Pub/Sub.
- Name — keep the default
Pub/Sub integrationor enter your own. - Enable integration and Allow create devices or assets — keep both on, so the device is created on its first uplink.
- Click Next.
- Uplink data converter:
- Click Select existing and choose
Pub/Sub Uplink Converter. - Alternatively, click Create new to write a decoder here.
- Click Next.
- Click Select existing and choose
- Connection:
- Project ID — enter your project ID, for example
tb-pubsub-integration. - Subscription ID — enter
tb-uplink-sub. - Service Account key file — click Browse file and select the key file, or click Use Secret storage and select a stored secret.
- Leave the other parameters at their defaults. See Connection Settings for details.
- Project ID — enter your project ID, for example
- Click Add.
Connection Settings
Section titled “Connection Settings”| Parameter | Description |
|---|---|
| Project ID | ID of the Google Cloud project with the subscription. |
| Subscription ID | Pull subscription to receive messages from. It must exist before the integration starts. |
| Service Account key file | JSON key of a service account with the Pub/Sub Subscriber role on the subscription. |
| Execute remotely | Runs the integration as a separate process outside the ThingsBoard cluster, for example in a network with restricted internet access. ThingsBoard generates an Integration key and Integration secret for it. See Remote Integration. |
| Description | In Advanced settings. Free-text description. |
| Metadata | In Advanced settings. Key–value pairs added to the metadata of every message from this integration. |
The integration does not offer Check connection: check the integration status after you add it.
Test Uplink
Section titled “Test Uplink”Publish a test message to the topic, then confirm that ThingsBoard decoded it and created the device.
Send a Test Uplink
Section titled “Send a Test Uplink”-
Go to Pub/Sub ⇾ Topics and open
tb-uplink. -
On the Messages tab, click Publish message.
-
Paste the payload into Message body and click Publish.
{"deviceName": "Sensor A1","deviceType": "thermostat","temperature": 23.5,"humidity": 60} -
Check that the console shows Message published.
Replace my-gcp-project with your project ID and run:
gcloud pubsub topics publish tb-uplink \ --message='{"deviceName":"Sensor A1","deviceType":"thermostat","temperature":23.5,"humidity":60}' \ --project=my-gcp-projectTo send message attributes, add --attribute=KEY=VALUE, for example --attribute=source=gateway-01.
Verify the Result
Section titled “Verify the Result”- Integration events — go to Integrations ⇾ Integrations, open the integration, and click the Events tab. The uplink appears with status OK. Click
⋅⋅⋅in the Message column to see the message data. - Converter events — open
Pub/Sub Uplink Converterin Integrations ⇾ Data converters and click the Events tab. In shows the message data, Out shows the decoded result, and Metadata showsintegrationName,pubSubMsgId, and the message attributes. - Device — go to Devices & assets ⇾ Devices. The integration has created Sensor A1 with the
thermostatprofile. Open it and checktemperatureandhumidityon the Latest telemetry tab.
Configure Downlink
Section titled “Configure Downlink”The integration does not send data to Pub/Sub. To publish ThingsBoard data to a topic — for example, a command for a device or an event for a cloud function — add a GCP Pub/Sub node to a rule chain. The node publishes the message data as is, and it works without the integration.
Create the Downlink Topic
Section titled “Create the Downlink Topic”Use a separate topic, so the integration does not receive its own messages.
- Go to Pub/Sub ⇾ Topics and click Create topic.
- Enter
tb-downlinkas the Topic ID. Keep Add a default subscription selected: you read the published messages fromtb-downlink-sub. - Click Create.
Grant Publish Access
Section titled “Grant Publish Access”The Pub/Sub Subscriber role does not allow publishing. Grant the service account the Pub/Sub Publisher role on the new topic.
- Open the
tb-downlinktopic, go to the Permissions tab, and click Add principal. - In New principals, enter the service account email, for example
tb-pubsub@tb-pubsub-integration.iam.gserviceaccount.com. - In Assign roles, select Pub/Sub Publisher and click Save.
To keep uplink and downlink credentials apart, create a second service account with only the Pub/Sub Publisher role and use its key in the node.
Configure the Root Rule Chain
Section titled “Configure the Root Rule Chain”- Go to Data processing ⇾ Rule chains and open the Root Rule Chain.
- From the External section, drag a gcp pubsub node onto the canvas.
- Set the node fields:
- Name — for example,
Publish to Pub/Sub. - GCP project ID — your project ID, for example
tb-pubsub-integration. - Topic name —
tb-downlink. - GCP service account key file — the key file of the service account.
- Message attributes — optional. Values can use
${metadataKey}for a message metadata value and$[messageKey]for a message data value, for exampledeviceName=${deviceName}. - Click Add.
- Name — for example,
- Connect the message type switch node to the gcp pubsub node with the Attributes Updated relation.
- Click Apply changes.
On success, the node adds the messageId returned by Pub/Sub to the message metadata. On failure, it routes the message through the Failure relation with the reason in metadata.error.
Test Downlink
Section titled “Test Downlink”- Go to Devices & assets ⇾ Devices, select Sensor A1, and open the Attributes tab.
- Select Shared attributes and click +.
- Set Key to
temperatureUploadFrequency, Type to Integer, Value to60, and click Add. ThingsBoard publishes the message.
Verify the Downlink
Section titled “Verify the Downlink”- Rule node events — turn on debug mode for the gcp pubsub node before the test. On its Events tab, the event has the Success relation. Data shows
{"temperatureUploadFrequency":60}, and Metadata showsmessageId. - Google Cloud console — go to Pub/Sub ⇾ Subscriptions, open
tb-downlink-sub, and on the Messages tab click Pull. The message appears with its body.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| Integration does not start | The key file is not a valid service account key. | Create a new JSON key and upload it. |
| No uplinks arrive | Wrong Project ID or Subscription ID, messages go to another topic, or the subscription uses push delivery or has expired. | Copy both IDs from the Google Cloud console and publish to the subscription’s topic. Set Delivery type to Pull. Recreate an expired subscription. |
| Same, with correct settings | The service account lacks the Pub/Sub Subscriber role, is disabled, or the Cloud Pub/Sub API is disabled. | Grant the role, enable the account, and enable the API. |
| Converter error; the message is gone | The data is not valid JSON, or the decoder fails on it. The integration has already acknowledged the message. | Check In and Error in the converter events, fix the decoder, and publish again. |
| Device not created | deviceName is empty, or Allow create devices or assets is off. |
Check Out in the converter events and the integration settings. |
Node routes to Failure with PERMISSION_DENIED |
The node’s service account lacks Pub/Sub Publisher on the topic. | Grant the role on tb-downlink. |
| Node has no events | The rule chain does not reach the gcp pubsub node. | Check the Attributes Updated relation and turn on debug mode for the node. |
To read debug events, open the integration or converter, click Events, and select an event: In is the received data, Out is the converter result, and Error holds the error text.
See Also
Section titled “See Also”- Integrations overview — how integrations, converters, uplinks, and downlinks fit together.
- Uplink data converter — decoder inputs, output format, and scripting patterns.
- GCP Pub/Sub node — publish messages from the Rule Engine to a Pub/Sub topic.
- Remote Integration — run the integration outside the ThingsBoard cluster.
- TBEL — built-in functions and syntax for converter scripts.
- Pub/Sub subscriptions — pull and push delivery, acknowledgment, and expiration.
Was this helpful?