Google Cloud Pub/Sub Integration
Google Cloud Pub/Sub is a fully managed publish/subscribe messaging service. The ThingsBoard Pub/Sub integration pulls messages from a GCP pull subscription, decodes them via an uplink converter, and stores telemetry and attributes in ThingsBoard.
Configure Google Cloud Pub/Sub
Section titled “Configure Google Cloud Pub/Sub”Before creating the integration in ThingsBoard, prepare the following in the Google Cloud Console.
Enable the Pub/Sub API
Section titled “Enable the Pub/Sub API”- Open your GCP project and navigate to APIs & Services ⇾ Library.
- Search for Cloud Pub/Sub API and click Enable if not already enabled.
Create a topic and subscription
Section titled “Create a topic and subscription”- Go to Pub/Sub ⇾ Topics and click Create topic.
- Enter a Topic ID (e.g.
tb-uplink) and click Create. - Open the new topic and click Create subscription in the Subscriptions tab.
- Enter a Subscription ID (e.g.
tb-uplink-sub), keep Delivery type set to Pull, and click Create.
Create a service account
Section titled “Create a service account”ThingsBoard authenticates to GCP using a service account key. The account needs at least the Pub/Sub Subscriber role to receive uplink messages.
- Go to IAM & Admin ⇾ Service accounts and click Create service account.
- Enter a name (e.g.
thingsboard-pubsub) and click Create and continue. - Assign the role Pub/Sub Subscriber and click Done.
- Open the created service account, go to the Keys tab, and click Add key ⇾ Create new key.
- Select JSON and click Create. Save the downloaded key file securely.
ThingsBoard integration setup
Section titled “ThingsBoard integration setup”Create an uplink converter
Section titled “Create an uplink converter”The uplink converter decodes incoming Pub/Sub messages and maps them to the ThingsBoard data model. Pub/Sub uses a generic uplink converter.
The decoder function receives:
payload— the raw Pub/Sub message data as a UTF-8 byte arraymetadata—integrationNameand any key-value pairs configured in the integration’s Metadata settings
Sample payload:
{ "deviceName": "Sensor A1", "deviceType": "thermostat", "temperature": 23.5, "humidity": 60}The decoder function used in this tutorial:
// 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 = {};var attributes = { model: 'Model A', serialNumber: 'SN111', integrationName: metadata['integrationName'],};
// Keys to exclude from telemetry (already used or non-telemetry fields)var excludeFromTelemetryList = ["deviceName", "deviceType", "ts", "timestamp"];
// 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, attributes: attributes, 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 = {};var attributes = { model: 'Model A', serialNumber: 'SN111', integrationName: metadata['integrationName'],};
// Keys to exclude from telemetry (already used or non-telemetry fields)var excludeFromTelemetryList = ["deviceName", "deviceType", "ts", "timestamp"];
// 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, attributes: attributes, 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 this converter to your device:
- Different device name / type fields — replace
data.deviceNameanddata.deviceTypewith the field names your device actually sends (e.g.data.id,data.sensorType). - Timestamp — if your payload includes a Unix millisecond timestamp, name the field
ts; for an ISO 8601 string, name ittimestamp. If neither is present, the converter falls back to the server receive time automatically. - Telemetry fields — all JSON fields not listed in
excludeFromTelemetryListare mapped to telemetry automatically viatoFlatMap. To exclude additional non-telemetry fields (e.g. a firmware version you want as an attribute), add their names to the exclude list. - Static attributes — replace
modelandserialNumberin theattributesobject with the actual device properties from your payload or hard-coded values; remove the entries if not needed. - Assets instead of devices — uncomment
assetName/assetTypeand comment outdeviceName/deviceTypeto provision assets instead of devices. - Customer or group assignment — uncomment
customerNameorgroupNameand set the appropriate values to assign the device to a customer or device group.
Create the integration
Section titled “Create the integration”- Go to Integrations center ⇾ Integrations and click + Add integration.
- Basic settings:
- Set Integration type to Pub/Sub.
- Enable integration and Allow create devices or assets are on by default.
- Click Next.
- Uplink data converter:
- Click Create new, enter a name, paste the decoder function from above, and click Add.
- Alternatively, click Select existing to reuse a previously created converter.
- Click Next.
- Connection:
- Project ID — your GCP project ID (e.g.
my-gcp-project). - Subscription ID — the pull subscription name (e.g.
tb-uplink-sub). - Service account key — upload the JSON key file via Browse file, or click Use Secret storage to reference a stored secret.
- See Connection settings for a full description of each parameter.
- Click Add to save the integration.
- Project ID — your GCP project ID (e.g.
Connection settings
Section titled “Connection settings”Project ID
Your Google Cloud project ID. Found in the project selector dropdown in the GCP Console or on the Project info card on the dashboard.
Subscription ID
The pull subscription name on the uplink topic (e.g. tb-uplink-sub). The subscription must exist in GCP before the integration is created. ThingsBoard reads messages through this subscription — it is already bound to the topic in GCP.
Service account key
The GCP service account key JSON file. The account must have at least roles/pubsub.subscriber on the uplink subscription. Upload the file using Browse file, or store it in ThingsBoard Secret storage and reference it via Use Secret storage.
Execute remotely
When enabled, ThingsBoard generates an Integration key and Integration secret that allow the integration to run as a separate process outside the ThingsBoard cluster — useful when access to GCP is restricted to a specific network. See Remote Integration.
Advanced settings
| Parameter | Description |
|---|---|
| Description | Optional text description for the integration. |
| Metadata | Key-value pairs injected into every uplink message as integrationMetadata in the converter script. |
Send a test uplink
Section titled “Send a test uplink”Publish a test message to the uplink topic using the gcloud CLI or the GCP Console.
gcloud pubsub topics publish tb-uplink \ --message='{"deviceName":"Sensor A1","deviceType":"thermostat","temperature":23.5,"humidity":60}' \ --project=my-gcp-project- Go to Pub/Sub ⇾ Topics and open the tb-uplink topic.
- Click the Messages tab, then click Publish message.
- Paste the JSON payload into the Message body field and click Publish.
{ "deviceName":"Sensor A1", "deviceType":"thermostat", "temperature":23.5, "humidity":60}Verify in ThingsBoard
Section titled “Verify in ThingsBoard”Check device telemetry — go to Entities ⇾ Devices. The device Sensor A1 is automatically provisioned on the first message. Open it and check the Latest telemetry tab — temperature and humidity should reflect the published values. The Attributes tab shows the client attributes set by the converter: integrationName, model, and serialNumber.
Integration events — go to Integrations center ⇾ Integrations, open the Pub/Sub integration, and check the Events tab. Click … in the Message column to inspect the raw payload pulled from the subscription.
Converter events — go to Integrations center ⇾ Data converters, click the uplink converter, and open its Events tab. Click … in the respective column to inspect each field:
- In — the raw payload passed to the converter.
- Out — the decoded result:
deviceName,deviceType,attributes(model, serialNumber, integrationName), andtelemetry(temperature, humidity). - Metadata —
integrationNameandpubSubMsgId(the unique message ID assigned by GCP Pub/Sub).
Publish to GCP Pub/Sub from ThingsBoard
Section titled “Publish to GCP Pub/Sub from ThingsBoard”The Pub/Sub integration is uplink-only — it does not have a dedicated downlink converter. To publish messages from ThingsBoard back to a GCP Pub/Sub topic, use the GCP Pub/Sub Rule Node in the rule engine. Configure it with your Project ID, target topic name, and service account key.
Troubleshooting
Section titled “Troubleshooting”Integration status is not Active
- Confirm the Project ID and Subscription ID exactly match the values in the GCP Console.
- Verify the Service account key JSON is complete and belongs to an active service account.
- Check that the service account has
roles/pubsub.subscriberon the subscription. - Ensure the Cloud Pub/Sub API is enabled in your GCP project.
Messages are not appearing in ThingsBoard
- Check the integration Events tab for errors.
- Confirm the pull subscription is attached to the correct topic and has no expiration policy that deactivated it.
- Use the Converter Events tab to inspect the raw In payload — verify the uplink converter handles the actual message format.