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
subclaim - Username: The
jticlaim - 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()