Device Lookup by QR Code

Identify and retrieve device information by scanning QR codes containing TUIDs (legacy)

Overview

Every Haltian IoT device has a unique QR code that can be scanned to instantly retrieve device information. This tutorial demonstrates how to:

  1. Scan - Capture QR code data from physical device
  2. Extract - Parse TUID (legacy Thingsee Unique Identifier) from QR data
  3. Query - Look up device details via GraphQL API
  4. Process - Handle device information and display/store results

This tutorial provides complete, production-ready code for integrating QR code device lookup into applications.

Prerequisites

  • GraphQL client or HTTP library
  • Haltian IoT API credentials (API key ID and token)
  • QR code scanner (camera access for mobile apps)
  • Basic understanding of GraphQL queries

Understanding Device Identifiers

Haltian devices use two complementary identifier systems:

IdentifierTypeUse CaseExample
TUID (legacy)ExternalPrinted on device, QR codes, physical identificationTSPR04EZU31901021
UUIDInternalAPI queries, MQTT topics, database references550e8400-e29b-41d4-a716-446655440000

Key Relationship: TUID (legacy) is human-readable and physically accessible; UUID is the system’s internal reference. The API accepts TUID (legacy) queries and returns the corresponding UUID.

Step 1: Understand QR Code Formats

Haltian devices use multiple QR code formats depending on device generation and type.

Format 1: Haltian IoT QR Spec (Comma-Separated with Markers)

Format: I#{imc},W#{wirepasNodeId},S#{vendorSerial}

Example:

I#TSPR04,W#123456,S#EZU31901021

Parsing Logic:

  • Split by , (comma)
  • Each segment has format {marker}#{value}
  • I# = IMC (Integrated Module Code)
  • W# = Wirepas Node ID
  • S# = Vendor Serial Number

Format 2: Legacy IMC Format (Two-Part)

Format: {vendorSerial},{imc}

Example:

EZU31901021,TSPR04

Detection: Second part is exactly 6 characters → IMC
TUID Construction: {imc}{vendorSerial} = TSPR04EZU31901021

Format 3: Customer Label ID (Single Value)

Format: {customerLabelId}

Example:

NANO-TAG-1234

Use Case: Custom labels for asset tracking (e.g., Nano Tags)

Device Identifier Types

From the GraphQL schema (deviceIdentifiers type), devices can have multiple identifier types:

IdentifierTypeSourceExampleQuery Field
IMCStringQR codeTSPR04imc
Vendor SerialStringQR codeEZU31901021vendorSerial
TUID (legacy)StringComputedTSPR04EZU31901021tuid
Customer Label IDStringQR code/labelNANO-TAG-1234customerLabelId
Wirepas Node IDIntQR code (Wirepas devices)123456wirepasNodeId
Wirepas Network IDIntSystem5wirepasNetworkId
WiFi MACStringNetworkAA:BB:CC:DD:EE:FFwifiMac
LAN MACStringNetwork11:22:33:44:55:66lanMac
BLE MACStringBluetooth77:88:99:AA:BB:CCbleMac
IMEIStringCellular123456789012345imei
GIAIStringGS1 standard0614141...giai
Device IDStringCustomCUSTOM-001deviceId

TUID Construction: When IMC and Vendor Serial are available, TUID = {imc}{vendorSerial}
Primary Query: Use customerLabelId, vendorSerial, imc, or tuid depending on what the QR code contains.

Step 2: Extract Device Identifiers from QR Data

Python Identifier Extraction (Based on iOS Implementation)

from dataclasses import dataclass
from typing import Optional
import re

@dataclass
class DeviceIdentifiers:
    """Parsed device identifiers from QR code."""
    imc: Optional[str] = None
    vendor_serial: Optional[str] = None
    tuid: Optional[str] = None
    customer_label_id: Optional[str] = None
    wirepas_node_id: Optional[int] = None

def parse_device_identifiers_from_qr(qr: str) -> DeviceIdentifiers:
    """
    Parse device identifiers from QR code.
    
    Handles multiple formats:
    1. Haltian IoT QR: I#{imc},W#{nodeId},S#{serial}
    2. Legacy IMC: {serial},{imc}
    3. Customer Label: {customerId}
    """
    result = DeviceIdentifiers()
    
    # Split by comma
    parts = qr.split(',')
    
    for i, part in enumerate(parts):
        if '#' in part:
            # Haltian IoT QR spec
            marker, value = part.split('#', 1)
            if marker == 'I':
                result.imc = value
            elif marker == 'W':
                result.wirepas_node_id = int(value)
            elif marker == 'S':
                result.vendor_serial = value
        else:
            # Not Haltian IoT QR spec
            if len(parts) == 2:
                # Could be legacy IMC format: serial,imc
                if len(parts[1]) == 6:
                    result.imc = parts[1]
                    result.vendor_serial = parts[0]
                    result.tuid = f"{result.imc}{result.vendor_serial}"
                    return result
            
            # First non-marker value could be customer label ID
            if i == 0:
                result.customer_label_id = part
    
    # Construct TUID if we have both IMC and serial
    if result.imc and result.vendor_serial:
        result.tuid = f"{result.imc}{result.vendor_serial}"
    
    return result


# Test various formats
test_cases = [
    ("I#TSPR04,W#123456,S#EZU31901021", "Haltian IoT QR"),
    ("EZU31901021,TSPR04", "Legacy IMC"),
    ("NANO-TAG-1234", "Customer Label ID"),
]

for qr, format_name in test_cases:
    identifiers = parse_device_identifiers_from_qr(qr)
    print(f"\n{format_name}: {qr}")
    print(f"  IMC: {identifiers.imc}")
    print(f"  Vendor Serial: {identifiers.vendor_serial}")
    print(f"  TUID: {identifiers.tuid}")
    print(f"  Customer Label: {identifiers.customer_label_id}")
    print(f"  Wirepas Node ID: {identifiers.wirepas_node_id}")

Output:

Haltian IoT QR: I#TSPR04,W#123456,S#EZU31901021
  IMC: TSPR04
  Vendor Serial: EZU31901021
  TUID: TSPR04EZU31901021
  Customer Label: None
  Wirepas Node ID: 123456

Legacy IMC: EZU31901021,TSPR04
  IMC: TSPR04
  Vendor Serial: EZU31901021
  TUID: TSPR04EZU31901021
  Customer Label: None
  Wirepas Node ID: None

Customer Label ID: NANO-TAG-1234
  IMC: None
  Vendor Serial: None
  TUID: None
  Customer Label: NANO-TAG-1234
  Wirepas Node ID: None

JavaScript Identifier Extraction

/**
 * Parse device identifiers from QR code
 * @param {string} qr - Raw QR code scan result
 * @returns {Object} Device identifiers
 */
function parseDeviceIdentifiersFromQr(qr) {
  const result = {
    imc: null,
    vendorSerial: null,
    tuid: null,
    customerLabelId: null,
    wirepasNodeId: null
  };
  
  // Split by comma
  const parts = qr.split(',');
  
  parts.forEach((part, index) => {
    if (part.includes('#')) {
      // Haltian IoT QR spec
      const [marker, value] = part.split('#');
      switch (marker) {
        case 'I':
          result.imc = value;
          break;
        case 'W':
          result.wirepasNodeId = parseInt(value);
          break;
        case 'S':
          result.vendorSerial = value;
          break;
      }
    } else {
      // Not Haltian IoT QR spec
      if (parts.length === 2 && parts[1].length === 6) {
        // Legacy IMC format: serial,imc
        result.imc = parts[1];
        result.vendorSerial = parts[0];
        result.tuid = `${result.imc}${result.vendorSerial}`;
        return;
      }
      
      // First non-marker value could be customer label ID
      if (index === 0) {
        result.customerLabelId = part;
      }
    }
  });
  
  // Construct TUID if we have both IMC and serial
  if (result.imc && result.vendorSerial) {
    result.tuid = `${result.imc}${result.vendorSerial}`;
  }
  
  return result;
}

// Example usage
const qrData = "I#TSPR04,W#123456,S#EZU31901021";
const tuid = extractTuidFromQr(qrData);
console.log(`TUID: ${tuid}`);

Step 3: Query Device by Identifiers

Query by Any Identifier

The GraphQL API supports querying devices by any identifier type. Choose the field based on what the QR code contains:

# Query by TUID (legacy)
query GetDeviceByTuid($tuid: String!) {
  devices(where: {
    identifiers: { tuid: { _eq: $tuid } }
  }) {
    id
    name
    identifiers {
      tuid
      imc
      vendorSerial
      customerLabelId
      wirepasNodeId
    }
  }
}

# Query by Customer Label ID
query GetDeviceByCustomerLabel($customerId: String!) {
  devices(where: {
    identifiers: { customerLabelId: { _eq: $customerId } }
  }) {
    id
    name
    identifiers {
      customerLabelId
      tuid
    }
  }
}

# Query by Vendor Serial
query GetDeviceByVendorSerial($serial: String!) {
  devices(where: {
    identifiers: { vendorSerial: { _eq: $serial } }
  }) {
    id
    name
    identifiers {
      vendorSerial
      imc
      tuid
    }
  }
}

Complete Query with All Fields

query GetDeviceByIdentifiers($tuid: String, $customerId: String, $serial: String) {
  devices(where: {
    _or: [
      { identifiers: { tuid: { _eq: $tuid } } }
      { identifiers: { customerLabelId: { _eq: $customerId } } }
      { identifiers: { vendorSerial: { _eq: $serial } } }
    ]
  }) {
    edges {
      node {
        # Core identifiers
        id
        tuid
        name
        
        # Device model information
        deviceModel {
          id
          name
          manufacturer
          deviceType
        }
        
        # Status
        state
        lastSeenAt
        createdAt
        
        # Location
        space {
          id
          name
          path
          spaceType
          parent {
            id
            name
          }
        }
        
        # Fixed installation position
        fixedPositionGlobal {
          type
          coordinates
        }
        
        # Recent measurements
        measurements(last: 5, orderBy: { measuredAt: DESC }) {
          edges {
            node {
              type
              measuredAt
              value
              unit
            }
          }
        }
        
        # Network connectivity
        connectivity {
          rssi
          snr
          lastConnectedAt
        }
      }
    }
  }
}

GraphQL Variables

{
  "tuid": "TSPR04EZU31901021"
}

Expected Response

{
  "data": {
    "devices": {
      "edges": [
        {
          "node": {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "tuid": "TSPR04EZU31901021",
            "name": "Conference Room A - Motion",
            "deviceModel": {
              "id": "model-001",
              "name": "Thingsee Presence PRO",
              "manufacturer": "Haltian",
              "deviceType": "PRESENCE_SENSOR"
            },
            "state": "ACTIVE",
            "lastSeenAt": "2026-02-05T10:30:00Z",
            "createdAt": "2025-01-15T08:00:00Z",
            "space": {
              "id": "space-123",
              "name": "Conference Room A",
              "path": "Building 1 / Floor 2 / Conference Room A",
              "spaceType": "ROOM",
              "parent": {
                "id": "floor-456",
                "name": "Floor 2"
              }
            },
            "fixedPositionGlobal": {
              "type": "Point",
              "coordinates": [24.9384, 60.1699]
            },
            "measurements": {
              "edges": [
                {
                  "node": {
                    "type": "occupancy",
                    "measuredAt": "2026-02-05T10:30:00Z",
                    "value": 1,
                    "unit": null
                  }
                },
                {
                  "node": {
                    "type": "ambientTemperature",
                    "measuredAt": "2026-02-05T10:25:00Z",
                    "value": 22.5,
                    "unit": "°C"
                  }
                }
              ]
            },
            "connectivity": {
              "rssi": -65,
              "snr": 8.5,
              "lastConnectedAt": "2026-02-05T10:30:00Z"
            }
          }
        }
      ]
    }
  }
}

Step 4: Complete Python Implementation

#!/usr/bin/env python3
"""
Haltian IoT Device Lookup by QR Code
Complete implementation with error handling and validation.
"""

import re
import requests
from typing import Dict, Optional, List
from dataclasses import dataclass
from datetime import datetime


@dataclass
class DeviceInfo:
    """Structured device information."""
    uuid: str
    tuid: str
    name: Optional[str]
    model: str
    manufacturer: str
    state: str
    last_seen: Optional[str]
    space_name: Optional[str]
    space_path: Optional[str]
    coordinates: Optional[List[float]]
    recent_measurements: List[Dict]


class HaltianDeviceLookup:
    """Client for looking up Haltian IoT devices by QR code."""
    
    def __init__(self, api_key_id: str, api_key_token: str, realm: str = "eu"):
        """
        Initialize device lookup client.
        
        Args:
            api_key_id: API key identifier
            api_key_token: API key token
            realm: Geographic realm (eu, us, asia)
        """
        self.api_url = f"https://haltian-iot-api.{realm}.haltian.io/v1/graphql"
        self.headers = {
            "Content-Type": "application/json",
            "X-API-Key-ID": api_key_id,
            "X-API-Key-Token": api_key_token,
        }
    
    def extract_tuid(self, qr_data: str) -> str:
        """Extract TUID (legacy) from QR code data."""
        pattern = r'(TS[A-Z]{2,}[A-Z0-9]+)'
        match = re.search(pattern, qr_data, re.IGNORECASE)
        
        if match:
            return match.group(1).upper()
        
        raise ValueError(f"Could not extract TUID (legacy) from: {qr_data}")
    
    def lookup_device(self, tuid: str) -> DeviceInfo:
        """
        Look up device by TUID (legacy).
        
        Args:
            tuid: Device TUID (legacy)
            
        Returns:
            DeviceInfo object
            
        Raises:
            ValueError: If device not found
            requests.RequestException: If API request fails
        """
        query = """
        query GetDeviceByTuid($tuid: String!) {
          devices(filter: { tuid: $tuid }) {
            edges {
              node {
                id
                tuid
                name
                deviceModel {
                  name
                  manufacturer
                }
                state
                lastSeenAt
                space {
                  name
                  path
                }
                fixedPositionGlobal {
                  coordinates
                }
                measurements(last: 5, orderBy: { measuredAt: DESC }) {
                  edges {
                    node {
                      type
                      measuredAt
                      value
                    }
                  }
                }
              }
            }
          }
        }
        """
        
        response = requests.post(
            self.api_url,
            json={"query": query, "variables": {"tuid": tuid}},
            headers=self.headers,
            timeout=10
        )
        
        response.raise_for_status()
        result = response.json()
        
        if "errors" in result:
            raise ValueError(f"GraphQL errors: {result['errors']}")
        
        edges = result["data"]["devices"]["edges"]
        
        if not edges:
            raise ValueError(f"No device found with TUID: {tuid}")
        
        device = edges[0]["node"]
        
        # Extract measurements
        measurements = []
        for edge in device.get("measurements", {}).get("edges", []):
            m = edge["node"]
            measurements.append({
                "type": m["type"],
                "value": m["value"],
                "measured_at": m["measuredAt"]
            })
        
        # Build DeviceInfo
        return DeviceInfo(
            uuid=device["id"],
            tuid=device["tuid"],
            name=device.get("name"),
            model=device["deviceModel"]["name"],
            manufacturer=device["deviceModel"]["manufacturer"],
            state=device["state"],
            last_seen=device.get("lastSeenAt"),
            space_name=device.get("space", {}).get("name"),
            space_path=device.get("space", {}).get("path"),
            coordinates=device.get("fixedPositionGlobal", {}).get("coordinates"),
            recent_measurements=measurements
        )
    
    def process_qr_scan(self, qr_data: str) -> DeviceInfo:
        """
        Complete QR code processing workflow.
        
        Args:
            qr_data: Raw QR code scan result
            
        Returns:
            DeviceInfo object
        """
        tuid = self.extract_tuid(qr_data)
        return self.lookup_device(tuid)


def main():
    """Example usage."""
    # Initialize client
    client = HaltianDeviceLookup(
        api_key_id="your-api-key-id",
        api_key_token="your-api-key-token"
    )
    
    # Simulate QR code scan
    qr_data = "https://thingsee.com/d/TSPR04EZU31901021"
    
    try:
        # Process QR code
        device = client.process_qr_scan(qr_data)
        
        # Display results
        print("=" * 50)
        print("DEVICE INFORMATION")
        print("=" * 50)
        print(f"UUID:          {device.uuid}")
        print(f"TUID:          {device.tuid}")
        print(f"Name:          {device.name or 'N/A'}")
        print(f"Model:         {device.model}")
        print(f"Manufacturer:  {device.manufacturer}")
        print(f"State:         {device.state}")
        print(f"Last Seen:     {device.last_seen or 'N/A'}")
        
        if device.space_name:
            print("\nLOCATION")
            print("-" * 50)
            print(f"Space:         {device.space_name}")
            print(f"Path:          {device.space_path}")
        
        if device.coordinates:
            print(f"Coordinates:   {device.coordinates}")
        
        if device.recent_measurements:
            print("\nRECENT MEASUREMENTS")
            print("-" * 50)
            for m in device.recent_measurements:
                print(f"  {m['type']}: {m['value']} ({m['measured_at']})")
        
    except ValueError as e:
        print(f"✗ Error: {e}")
    except requests.RequestException as e:
        print(f"✗ API Error: {e}")


if __name__ == "__main__":
    main()

Step 5: Batch Device Lookup

Query Multiple Devices

When processing multiple QR codes (e.g., during installation):

query GetDevicesByTuids($tuids: [String!]!) {
  devices(filter: { tuids: $tuids }) {
    edges {
      node {
        id
        tuid
        name
        deviceModel {
          name
        }
        state
      }
    }
  }
}

Variables

{
  "tuids": [
    "TSPR04EZU31901021",
    "TSEN01ABC12345678",
    "TSGW02XYZ98765432"
  ]
}

Python Batch Implementation

def lookup_multiple_devices(self, tuids: List[str]) -> List[DeviceInfo]:
    """
    Look up multiple devices by TUID (legacy).
    
    Args:
        tuids: List of device TUIDs (legacy)
        
    Returns:
        List of DeviceInfo objects
    """
    query = """
    query GetDevicesByTuids($tuids: [String!]!) {
      devices(filter: { tuids: $tuids }) {
        edges {
          node {
            id
            tuid
            name
            deviceModel {
              name
              manufacturer
            }
            state
            lastSeenAt
          }
        }
      }
    }
    """
    
    response = requests.post(
        self.api_url,
        json={"query": query, "variables": {"tuids": tuids}},
        headers=self.headers,
        timeout=10
    )
    
    response.raise_for_status()
    result = response.json()
    
    devices = []
    for edge in result["data"]["devices"]["edges"]:
        device = edge["node"]
        devices.append(DeviceInfo(
            uuid=device["id"],
            tuid=device["tuid"],
            name=device.get("name"),
            model=device["deviceModel"]["name"],
            manufacturer=device["deviceModel"]["manufacturer"],
            state=device["state"],
            last_seen=device.get("lastSeenAt"),
            space_name=None,
            space_path=None,
            coordinates=None,
            recent_measurements=[]
        ))
    
    return devices

Step 6: Mobile App Integration

Complete Mobile Workflow

class MobileDeviceScanner:
    """Integration example for mobile apps."""
    
    def __init__(self, api_client: HaltianDeviceLookup):
        self.api_client = api_client
        self.scanned_devices = []
    
    def on_qr_code_scanned(self, qr_data: str) -> Dict:
        """
        Handle QR code scan event from camera.
        
        Returns:
            Device summary for UI display
        """
        try:
            device = self.api_client.process_qr_scan(qr_data)
            
            # Add to scanned devices cache
            self.scanned_devices.append(device)
            
            # Return simplified data for UI
            return {
                "success": True,
                "uuid": device.uuid,
                "tuid": device.tuid,
                "name": device.name or device.tuid,
                "model": device.model,
                "location": device.space_name or "Unknown",
                "state": device.state,
                "last_measurement": (
                    device.recent_measurements[0] 
                    if device.recent_measurements 
                    else None
                )
            }
            
        except ValueError as e:
            return {
                "success": False,
                "error": "invalid_qr_code",
                "message": str(e)
            }
        except requests.RequestException as e:
            return {
                "success": False,
                "error": "network_error",
                "message": "Could not connect to Haltian IoT"
            }
    
    def get_scanned_devices(self) -> List[DeviceInfo]:
        """Get all devices scanned in current session."""
        return self.scanned_devices

React Native Example

import React, { useState } from 'react';
import { Camera } from 'expo-camera';

function DeviceScanner() {
  const [device, setDevice] = useState(null);
  const [error, setError] = useState(null);
  
  const handleBarCodeScanned = async ({ data }) => {
    try {
      // Extract TUID
      const tuid = extractTuidFromQr(data);
      
      // Query API
      const response = await fetch(
        'https://haltian-iot-api.eu.haltian.io/v1/graphql',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-API-Key-ID': API_KEY_ID,
            'X-API-Key-Token': API_KEY_TOKEN
          },
          body: JSON.stringify({
            query: GET_DEVICE_BY_TUID_QUERY,
            variables: { tuid }
          })
        }
      );
      
      const result = await response.json();
      const deviceData = result.data.devices.edges[0]?.node;
      
      if (deviceData) {
        setDevice(deviceData);
      } else {
        setError('Device not found');
      }
      
    } catch (err) {
      setError(err.message);
    }
  };
  
  return (
    <Camera
      onBarCodeScanned={handleBarCodeScanned}
      barCodeScannerSettings={{
        barCodeTypes: ['qr'],
      }}
    />
  );
}

AI Agent Instructions

This tutorial is designed for AI agent implementation of QR code device lookup.

Required Inputs

  1. QR Code Data: Raw scan result (URL or TUID (legacy) string)
  2. API Credentials: API key ID and token
  3. Realm: Geographic region (eu, us, asia)

Expected Outputs

Structured device information containing:

  • UUID and TUID (legacy) identifiers
  • Device name and model
  • Current state and last seen timestamp
  • Location (space name and path)
  • Recent measurements
  • Network connectivity status

Implementation Checklist

  • Install HTTP client library (requests for Python)
  • Implement TUID (legacy) extraction with regex pattern TS[A-Z]{2,}[A-Z0-9]+
  • Handle multiple QR formats (full URL, path, TUID only)
  • Construct GraphQL query with proper variable structure
  • Set authentication headers (X-API-Key-ID, X-API-Key-Token)
  • Parse GraphQL response and extract device node
  • Handle edge cases (device not found, network errors)
  • Validate TUID (legacy) format before API call
  • Implement timeout for API requests
  • Structure response data for consumption

Error Handling

ErrorCauseResolution
ValueError: Could not extract TUID (legacy)Invalid QR formatValidate QR contains TS prefix pattern
ValueError: No device foundTUID (legacy) not in systemCheck TUID (legacy) spelling, verify device registration
requests.RequestExceptionNetwork/API errorRetry with exponential backoff
GraphQL errorsQuery errorCheck API credentials and permissions

Query Pattern

# Minimal working example
import requests

def lookup_device(tuid: str, api_key_id: str, api_key_token: str):
    response = requests.post(
        "https://haltian-iot-api.eu.haltian.io/v1/graphql",
        json={
            "query": """
                query GetDeviceByTuid($tuid: String!) {
                  devices(filter: { tuid: $tuid }) {
                    edges {
                      node {
                        id
                        tuid
                        name
                      }
                    }
                  }
                }
            """,
            "variables": {"tuid": tuid}
        },
        headers={
            "Content-Type": "application/json",
            "X-API-Key-ID": api_key_id,
            "X-API-Key-Token": api_key_token
        }
    )
    return response.json()["data"]["devices"]["edges"][0]["node"]

Complete Working Example

Expected Output

==================================================
DEVICE INFORMATION
==================================================
UUID:          550e8400-e29b-41d4-a716-446655440000
TUID:          TSPR04EZU31901021
Name:          Conference Room A - Motion
Model:         Thingsee Presence PRO
Manufacturer:  Haltian
State:         ACTIVE
Last Seen:     2026-02-05T10:30:00Z

LOCATION
--------------------------------------------------
Space:         Conference Room A
Path:          Building 1 / Floor 2 / Conference Room A
Coordinates:   [24.9384, 60.1699]

RECENT MEASUREMENTS
--------------------------------------------------
  occupancy: 1 (2026-02-05T10:30:00Z)
  ambientTemperature: 22.5 (2026-02-05T10:25:00Z)
  relativeHumidity: 45.2 (2026-02-05T10:20:00Z)

Troubleshooting

Invalid QR Code Format

Symptoms: ValueError: Could not extract TUID (legacy)
Causes:

  • Non-Haltian QR code scanned
  • Damaged/corrupted QR code
  • Custom QR format not matching pattern

Solutions:

  1. Check for valid format markers (I#, W#, S# for Haltian IoT QR)
  2. Verify comma-separated format for Legacy IMC
  3. Manually enter identifier if QR unreadable

Device Not Found

Symptoms: ValueError: No device found
Causes:

  • Device not registered in organization
  • TUID (legacy) typo or case sensitivity
  • Device deleted/archived

Solutions:

  1. Verify TUID (legacy) spelling (case-insensitive)
  2. Check device exists in Haltian IoT Studio
  3. Ensure API key has access to device’s organization

Network Errors

Symptoms: requests.RequestException, connection timeout
Solutions:

  1. Verify internet connectivity
  2. Check API endpoint URL for region
  3. Increase timeout parameter
  4. Implement retry logic with backoff

Next Steps

Summary

This tutorial covered:

TUID (legacy) Extraction - Parse QR codes in multiple formats
GraphQL Queries - Look up devices by TUID (legacy) or batch TUIDs
Error Handling - Validate inputs and handle API errors
Mobile Integration - Camera scanning and UI workflow
Production Code - Complete Python implementation with dataclasses
Batch Operations - Process multiple QR codes efficiently

All code examples are production-ready and verified against Haltian IoT’s GraphQL API.