Skip to content

Tendrl MQTT Client Guide

Overview

This guide provides a comprehensive explanation of how to connect to and use the Tendrl MQTT server across different programming languages.

Authentication and Connection

Entity Information Retrieval - Endpoint: https://app.tendrl.com/api/claims - Method: GET - Authentication: Bearer Token (API Key)

Connection Parameters

  • Client ID: Derived from the sub claim
  • Username: The jti claim
  • Password: The original API key

MQTT Connection Hosts

  • MQTT over TCP: https://mqtt.tendrl.com
  • Port: 443 (TLS)
  • MQTT over WebSocket: https://mqtt-ws.tendrl.com/ws
  • Port: 443 (TLS) - MQTT over WebSocket: Web and browser-friendly connection

Topic Structure

  • Publish Topic: {account}/{region}/{jti}/publish
  • Messages Topic: {account}/{region}/{jti}/messages

Message Structure

When working with Tendrl MQTT, messages follow a standardized structure:

Field Type Description Required Example
msg_type string Type of message (publish or heartbeat) Yes "publish" or "heartbeat"
data dict/str Full payload of the message Yes {"temperature": 25.5, "humidity": 60}
tags list of strings Optional tags associated with the message No ["sensor", "environment", "indoor"]
timestamp string ISO 8601 formatted timestamp of the message No "2023-11-15T14:30:22.123Z"
entity string Optional entity identifier No "temperature:sensor:01"

Timestamp Handling

  • If no timestamp is provided, Tendrl will assign a timestamp at message ingestion
  • Ingestion timestamp will be slightly less precise than the original timestamp
  • Recommended to include a timestamp for most accurate tracking

Auto Config with API Example

import requests
import json

class MQTTClient:
    def __init__(self, api_key, debug=False):
        # Fetch entity information
        self.entity_info = self.fetch_entity_info(api_key)

        # Construct connection parameters
        self.client_id = self.entity_info['sub']
        self.username = self.entity_info['jti']
        self.password = api_key

        # Connect to MQTT broker
        self.connect()

    def fetch_entity_info(self, api_key):
        # Validate API key
        if not api_key:
            raise ValueError("API key is required")

        # Default app URL
        app_url = "https://app.tendrl.com"

        # Prepare request headers
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }

        try:
            # Make API request to retrieve entity claims
            url = f'{app_url}/api/claims'
            response = requests.get(url, headers=headers)

            # Check response status
            if response.status_code != 200:
                raise ValueError(f"API request failed with status {response.status_code}")

            # Parse response
            entity_info = response.json()

            # Validate required claims
            if not entity_info.get('jti'):
                raise ValueError("Missing 'jti' (JSON Token Identifier) in API response")

            if not entity_info.get('sub'):
                raise ValueError("Missing 'sub' (Subject) in API response")

            return entity_info

        except Exception as e:
            raise ValueError(f"Error retrieving entity info: {e}")

Message Callback Handling

Implementing a Message Callback

def process_message(message):
    # Accessing message components
    payload = message['data']
    tags = message.get('tags', [])
    source = message['source']
    timestamp = message['timestamp']

    # Example processing logic
    print(f"Received message from {source}")
    print(f"Timestamp: {timestamp}")
    print(f"Tags: {tags}")

    # Process payload based on its content
    if isinstance(payload, dict):
        if 'temperature' in payload:
            handle_temperature_reading(payload)
        elif 'status' in payload:
            handle_device_status(payload)

# Example message structures
example_message = {
    "msg_type": "publish",
    "data": {"temperature": 25.5, "humidity": 60},
    "tags": ["sensor", "environment"],
    "source": "smart_home:living_room:temp_sensor_01",
    "timestamp": "2023-11-15T14:30:22.123Z"
}

Troubleshooting

  • Ensure network connectivity
  • Verify API key permissions

Compatibility

This guide is designed to be language-agnostic. Adapt the core principles to your specific programming environment.

Paho MQTT Client Example

Complete Example with Paho MQTT Library

import paho.mqtt.client as mqtt
import requests
import json
import ssl
import time
from datetime import datetime

class TendrlMQTTClient:
    def __init__(self, api_key, debug=False):
        """
        Initialize Tendrl MQTT Client with API key authentication
        """
        # Fetch entity information from Tendrl API
        self.entity_info = self._fetch_entity_info(api_key)

        # Parse account and region from sub claim
        sub_parts = self.entity_info['sub'].split(':')
        if len(sub_parts) < 3:
            raise ValueError("Invalid sub claim format. Expected 'account:region:prefix'")

        self.account = sub_parts[0]
        self.region = sub_parts[1]

        # MQTT Connection Parameters
        self.client_id = self.entity_info['sub']
        self.username = self.entity_info['jti']
        self.password = api_key

        # MQTT Broker Configuration
        self.mqtt_host = "mqtt.tendrl.com"
        self.mqtt_port = 443

        # Topic Configuration
        self.publish_topic = f"{self.account}/{self.region}/{self.username}/publish"
        self.messages_topic = f"{self.account}/{self.region}/{self.username}/messages"

        # Create MQTT Client
        self.client = mqtt.Client(client_id=self.client_id, protocol=mqtt.MQTTv311)

        # Configure TLS and Credentials
        self.client.tls_set(
            cert_reqs=ssl.CERT_REQUIRED,
            tls_version=ssl.PROTOCOL_TLS
        )
        self.client.username_pw_set(username=self.username, password=self.password)

        # Set Callbacks
        self.client.on_connect = self._on_connect
        self.client.on_message = self._on_message

    def _fetch_entity_info(self, api_key):
        """
        Retrieve entity information from Tendrl API
        """
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }

        response = requests.get('https://app.tendrl.com/api/claims', headers=headers)
        response.raise_for_status()
        return response.json()

    def connect(self):
        """
        Establish connection to MQTT broker
        """
        self.client.connect(self.mqtt_host, self.mqtt_port, keepalive=60)
        self.client.loop_start()

    def publish(self, data, tags=None, entity=None):
        """
        Publish a message to Tendrl MQTT topic

        :param data: Message payload
        :param tags: Optional list of tags
        :param entity: Optional entity identifier
        """
        message = {
            "msg_type": "publish",
            "data": data,
            "tags": tags or [],
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "entity": entity
        }

        self.client.publish(self.publish_topic, json.dumps(message))

    def _on_connect(self, client, userdata, flags, rc):
        """
        Connection callback
        """
        if rc == 0:
            print("Connected successfully")
            # Subscribe to messages topic
            client.subscribe(self.messages_topic)
        else:
            print(f"Connection failed with code {rc}")

    def _on_message(self, client, userdata, message):
        """
        Message receive callback
        """
        try:
            msg = json.loads(message)

            # Example message processing
            if isinstance(msg.get('data'), dict):
                if 'temperature' in msg['data']:
                    self._process_temperature(msg)
        except Exception as e:
            print(f"Message processing error: {e}")

    def _process_temperature(self, message):
        """
        Example temperature message handler
        """
        temp = message['data']['temperature']
        print(f"Temperature reading: {temp}°C")

def main():
    # Replace with your actual Tendrl API key
    API_KEY = "your_tendrl_api_key"

    try:
        # Initialize and connect MQTT client
        mqtt_client = TendrlMQTTClient(API_KEY)
        mqtt_client.connect()

        # Publish example sensor data
        mqtt_client.publish(
            data={"temperature": 25.5, "humidity": 60},
            tags=["sensor", "environment"],
            entity="smart_home:living_room:temp_sensor"
        )

        # Keep connection open to receive messages
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("Stopping MQTT client...")

    except Exception as e:
        print(f"MQTT Client Error: {e}")

if __name__ == "__main__":
    main()