Datacake LNS Nodes

LoRaWAN Network Server integration nodes for monitoring gateway status and performance from The Things Stack. More LNS to follow.

Overview

The Datacake LNS (LoRaWAN Network Server) nodes provide seamless integration with The Things Stack (TTS) API, enabling you to:

  • 🌐 Monitor gateway connection status in real-time

  • 📊 Track uplink and downlink message counters

  • ⏱️ Measure network latency and round-trip times

  • 📍 Retrieve gateway location information

  • 🔄 Query gateway details on-demand or at intervals

  • 🚨 Build alerting systems for gateway offline events

Perfect for LoRaWAN network operators, IoT service providers, and anyone managing TTS gateways who needs programmatic access to gateway status.


Configuration

TTS Config

Configuration node for storing The Things Stack API credentials securely.

Settings

  • Name - Optional descriptive name for this configuration

  • Server URL - The URL of your TTS server

    • Community Edition: https://eu1.cloud.thethings.network

    • Community Edition (US): https://nam1.cloud.thethings.network

    • Community Edition (AU): https://au1.cloud.thethings.network

    • Enterprise/Private: Your instance URL (e.g., https://thethings.example.com)

  • API Key - Your TTS API key with gateway read permissions

How to Get Your API Key

  1. Go to your application or user settings

  2. Navigate to API Keys

  3. Click Add API Key

  4. Required Rights:

    • RIGHT_GATEWAY_INFO - Read gateway information

    • RIGHT_GATEWAY_STATUS_READ - Read gateway status and statistics

  5. Generate and copy the API key

  6. Store it in the TTS Config node

⚠️ Important: Keep your API key secure. Never share it or commit it to version control.

Server URL Examples

  • The Things Network (Community):

    • Europe: https://eu1.cloud.thethings.network

    • North America: https://nam1.cloud.thethings.network

    • Australia: https://au1.cloud.thethings.network

  • The Things Stack (Enterprise/Cloud):

    • Your dedicated instance URL

    • Format: https://<tenant>.thethings.industries or custom domain

  • Private Deployment:

    • Your self-hosted instance URL


TTS Gateway Node

Monitor LoRaWAN gateway status and connection statistics from The Things Stack.

Configuration

Gateway ID Source

  • Configured - Use a fixed gateway ID from the node configuration

  • From Message - Extract gateway ID dynamically from incoming messages

Gateway ID

The gateway ID to query (only visible when "Gateway ID Source" is set to "Configured").

Gateway ID Format:

  • Usually starts with eui- followed by the gateway EUI

  • Example: eui-b827ebfffe8b1234

  • Found in The Things Stack Console under your gateway details

Input Properties

When Gateway ID Source is "Configured":

  • msg.payload - Any input triggers the query

When Gateway ID Source is "From Message":

  • msg.payload.gatewayId - Gateway ID to query (preferred)

  • msg.gatewayId - Alternative location for gateway ID

  • msg.payload (string) - Gateway ID as direct payload

Output Properties

Property
Type
Description

msg.payload

object

Normalized gateway status data

msg.gatewayId

string

The gateway ID that was queried

Output Structure

{
  "gateway_id": "eui-b827ebfffe8b1234",
  "gateway_eui": "B827EBFFFE8B1234",
  "name": "Office Building Gateway",
  "description": "Main gateway for office building IoT sensors",
  
  "location": {
    "latitude": 52.52,
    "longitude": 13.405,
    "altitude": 45,
    "source": "SOURCE_REGISTRY"
  },
  "latitude": 52.52,
  "longitude": 13.405,
  "altitude": 45,
  
  "frequency_plan_id": "EU_863_870_TTN",
  "frequency_plan_ids": ["EU_863_870_TTN"],
  "status_public": true,
  "location_public": true,
  
  "attributes": {
    "placement": "indoor",
    "antenna_type": "omni"
  },
  
  "version_ids": {
    "brand_id": "dragino",
    "model_id": "dlos8",
    "hardware_version": "1.0",
    "firmware_version": "4.4.3"
  },
  
  "connected": true,
  "connected_at": "2025-10-08T08:15:30.123Z",
  "disconnected_at": null,
  "protocol": "udp",
  
  "uplink_count": 15234,
  "downlink_count": 127,
  "last_uplink_received_at": "2025-10-08T10:35:22.456Z",
  "last_downlink_received_at": "2025-10-08T10:30:15.789Z",
  
  "round_trip_times": {
    "min": "12ms",
    "max": "89ms",
    "median": "34ms",
    "count": 1250
  },
  
  "sub_bands": [
    {
      "min_frequency": "863000000",
      "max_frequency": "870000000",
      "downlink_utilization": 0.02
    }
  ],
  
  "_raw": {
    "info": { /* Complete gateway info response */ },
    "stats": { /* Complete connection stats response */ }
  }
}

Status Display

The node provides real-time visual feedback:

Status
Indicator
Description

Connected

🟢 Green dot

Gateway is online with uplink/downlink counts shown

Disconnected

🟡 Yellow ring

Gateway is offline or not reporting

Error

🔴 Red ring

Error occurred (API error, invalid config, etc.)

Fetching

🔵 Blue dot

Query in progress

Example Status:

  • Connected (↑15234 ↓127) - Online with 15,234 uplinks and 127 downlinks

Connection Status Logic

A gateway is considered connected when:

  • connected_at exists (has connected at least once)

  • AND disconnected_at is null (not explicitly disconnected)

If connection stats are not available (404 response), the gateway is marked as not connected.


Use Cases & Examples

1. Periodic Gateway Monitoring

Monitor gateway status at regular intervals and alert when offline.

Flow:

[Inject: Every 5 minutes]

[TTS Gateway Node]
  Gateway ID: eui-b827ebfffe8b1234

[Switch: Check connection status]
  Property: msg.payload.connected

[Branch 1: If false]

  [Function: Format alert]
    msg.payload = {
      subject: "Gateway Offline Alert",
      text: `Gateway ${msg.payload.name} is offline.
             Last heard: ${msg.payload.disconnected_at}
             Gateway ID: ${msg.payload.gateway_id}`
    };
    return msg;

  [Email/SMS/Slack Alert]
  
[Branch 2: If true]

  [Debug: Log status (optional)]

Benefits:

  • Early detection of gateway failures

  • Proactive maintenance

  • Reduced downtime

  • Better network reliability


2. Gateway Performance Dashboard

Create a real-time dashboard showing gateway metrics.

Flow:

[Inject: Every 1 minute]

[TTS Gateway Node]

[Function: Extract metrics]
    msg.status = msg.payload.connected ? "Online" : "Offline";
    msg.uplinks = msg.payload.uplink_count || 0;
    msg.downlinks = msg.payload.downlink_count || 0;
    msg.latency = msg.payload.round_trip_times?.median || "N/A";
    msg.lastSeen = msg.payload.last_uplink_received_at;
    return msg;

[Dashboard Nodes]
  - Text: Gateway Status
  - Gauge: Uplink Count
  - Gauge: Downlink Count
  - Text: Median Latency
  - Text: Last Seen

Dashboard Widgets:

  • Connection status indicator (green/red)

  • Uplink/downlink counters

  • Latency gauge

  • Last activity timestamp

  • Signal strength trends


3. Multi-Gateway Fleet Monitoring

Monitor multiple gateways and aggregate status.

Flow:

[Inject: Every 5 minutes]

[Function: Generate gateway list]
    msg.payload = [
      "eui-b827ebfffe8b1234",
      "eui-a827ebfffe9c5678",
      "eui-c827ebfffe7d9012"
    ];
    return msg;

[Split: One message per gateway]

[Change: Set gateway ID]
    Set msg.payload to msg.payload (the gateway ID)

[TTS Gateway Node]
    Gateway ID Source: From Message

[Join: Combine all results]
    Mode: automatic
    After number of message parts: 3

[Function: Aggregate statistics]
    const gateways = msg.payload;
    const total = gateways.length;
    const online = gateways.filter(g => g.connected).length;
    const offline = total - online;
    const totalUplinks = gateways.reduce((sum, g) => sum + (g.uplink_count || 0), 0);
    
    msg.payload = {
      total: total,
      online: online,
      offline: offline,
      onlinePercentage: ((online / total) * 100).toFixed(2),
      totalUplinks: totalUplinks,
      gateways: gateways
    };
    return msg;

[Dashboard/Database/Alert]

Use Cases:

  • Fleet overview dashboard

  • Aggregate network statistics

  • Identify problematic gateways

  • Coverage analysis


4. Gateway Offline Alert System

Send notifications only when gateway goes offline (avoid spam).

Flow:

[Inject: Every 2 minutes]

[TTS Gateway Node]

[Function: Track status changes]
    const gatewayId = msg.payload.gateway_id;
    const currentStatus = msg.payload.connected;
    const previousStatus = flow.get(`gateway_${gatewayId}_status`);
    
    // Store current status
    flow.set(`gateway_${gatewayId}_status`, currentStatus);
    
    // Only continue if status changed to offline
    if (previousStatus === true && currentStatus === false) {
      msg.statusChanged = true;
      msg.alertType = "offline";
      return msg;
    } else if (previousStatus === false && currentStatus === true) {
      msg.statusChanged = true;
      msg.alertType = "online";
      return msg;
    }
    return null; // No change, don't send alert

[Switch: Route by alert type]

[Function: Format offline alert]
    msg.payload = {
      priority: "high",
      subject: `🚨 Gateway Offline: ${msg.payload.name}`,
      text: `Gateway has gone offline.
             
             Gateway: ${msg.payload.name}
             Gateway ID: ${msg.payload.gateway_id}
             Location: ${msg.payload.latitude}, ${msg.payload.longitude}
             Disconnected: ${msg.payload.disconnected_at}
             Last uplink: ${msg.payload.last_uplink_received_at}
             
             Please investigate immediately.`
    };
    return msg;

[Email/SMS Alert]
  
[Function: Format online alert]
    msg.payload = {
      subject: `✅ Gateway Online: ${msg.payload.name}`,
      text: `Gateway has come back online.
             
             Gateway: ${msg.payload.name}
             Connected: ${msg.payload.connected_at}
             Total uplinks: ${msg.payload.uplink_count}`
    };
    return msg;

[Email Notification]

Benefits:

  • No alert spam (only on status changes)

  • Tracks recovery automatically

  • Provides context in alerts

  • Easy to see disconnection duration


5. Gateway Health Check Report

Generate daily reports on gateway performance.

Flow:

[Inject: Daily at 8 AM]

[TTS Gateway Node]

[Function: Calculate 24h metrics]
    const gateway = msg.payload;
    const uplinks = gateway.uplink_count;
    const previousUplinks = flow.get('previous_uplink_count') || uplinks;
    const uplinksToday = uplinks - previousUplinks;
    
    flow.set('previous_uplink_count', uplinks);
    
    msg.payload = {
      gateway: gateway.name,
      gatewayId: gateway.gateway_id,
      status: gateway.connected ? "Online ✅" : "Offline ❌",
      uplinksToday: uplinksToday,
      totalUplinks: uplinks,
      totalDownlinks: gateway.downlink_count,
      latencyMedian: gateway.round_trip_times?.median || "N/A",
      lastSeen: gateway.last_uplink_received_at,
      location: `${gateway.latitude}, ${gateway.longitude}`
    };
    return msg;

[Function: Format email report]
    msg.payload = {
      to: "[email protected]",
      subject: `Daily Gateway Health Report - ${msg.payload.gateway}`,
      text: `Gateway Health Report
             
             Gateway: ${msg.payload.gateway}
             ID: ${msg.payload.gatewayId}
             Status: ${msg.payload.status}
             
             📊 Last 24 Hours:
             - Uplinks received: ${msg.payload.uplinksToday}
             - Median latency: ${msg.payload.latencyMedian}
             - Last activity: ${msg.payload.lastSeen}
             
             📈 Totals:
             - Total uplinks: ${msg.payload.totalUplinks}
             - Total downlinks: ${msg.payload.totalDownlinks}
             
             📍 Location: ${msg.payload.location}`
    };
    return msg;

[Email Node]

Report Contents:

  • Gateway status

  • 24-hour statistics

  • Latency metrics

  • Activity timestamps

  • Location info


6. Coverage Analysis

Map gateway locations and coverage areas.

Flow:

[Inject: On demand]

[Function: List all gateways]
    msg.payload = [
      "eui-gateway-1",
      "eui-gateway-2",
      "eui-gateway-3"
    ];
    return msg;

[Split]

[TTS Gateway Node]
    Gateway ID Source: From Message

[Join]

[Function: Extract locations]
    const gateways = msg.payload;
    
    msg.payload = gateways.map(g => ({
      name: g.name,
      id: g.gateway_id,
      latitude: g.latitude,
      longitude: g.longitude,
      altitude: g.altitude,
      connected: g.connected,
      uplinks: g.uplink_count
    }));
    return msg;

[Worldmap Node or Export to GeoJSON]

Use Cases:

  • Visualize gateway coverage

  • Plan gateway deployments

  • Identify coverage gaps

  • Optimize gateway placement


7. Latency Monitoring and Alerting

Monitor network latency and alert on degradation.

Flow:

[Inject: Every 5 minutes]

[TTS Gateway Node]

[Function: Check latency]
    const rtt = msg.payload.round_trip_times;
    if (!rtt) {
      return null; // No RTT data
    }
    
    // Extract median latency (remove 'ms')
    const latencyMs = parseInt(rtt.median);
    
    // Define thresholds
    const WARNING_THRESHOLD = 100; // ms
    const CRITICAL_THRESHOLD = 200; // ms
    
    msg.latency = latencyMs;
    
    if (latencyMs > CRITICAL_THRESHOLD) {
      msg.severity = "critical";
      msg.color = "red";
    } else if (latencyMs > WARNING_THRESHOLD) {
      msg.severity = "warning";
      msg.color = "yellow";
    } else {
      msg.severity = "normal";
      msg.color = "green";
      return null; // Don't alert on normal latency
    }
    
    return msg;

[Function: Format latency alert]
    msg.payload = {
      subject: `${msg.severity.toUpperCase()}: High Latency Detected`,
      text: `Gateway ${msg.payload.name} showing high latency:
             
             Current median latency: ${msg.latency}ms
             Min: ${msg.payload.round_trip_times.min}
             Max: ${msg.payload.round_trip_times.max}
             Samples: ${msg.payload.round_trip_times.count}
             
             Severity: ${msg.severity}
             Gateway: ${msg.payload.gateway_id}`
    };
    return msg;

[Email/SMS Alert]

Thresholds:

  • Normal: < 100ms

  • Warning: 100-200ms

  • Critical: > 200ms


Advanced Features

Dynamic Gateway Queries

Query different gateways based on runtime conditions:

// Query gateway based on device's gateway
msg.gatewayId = msg.payload.rx_metadata[0].gateway_ids.gateway_id;

Batch Gateway Monitoring

Monitor many gateways efficiently:

// Generate list from database or API
const gateways = flow.get('gateway_list');
msg.payload = gateways.map(g => g.gateway_id);
// Use Split + TTS Gateway + Join

Historical Tracking

Store gateway metrics over time:

// Store in database
const record = {
  timestamp: new Date(),
  gateway_id: msg.payload.gateway_id,
  connected: msg.payload.connected,
  uplink_count: msg.payload.uplink_count,
  median_latency: parseInt(msg.payload.round_trip_times?.median || 0)
};

// Save to InfluxDB, PostgreSQL, etc.

Best Practices

Polling Intervals

Use Case
Recommended Interval
Reason

Real-time monitoring

1-2 minutes

Catch issues quickly

General monitoring

5-10 minutes

Balance freshness and API calls

Health checks

15-30 minutes

Reduce load, still responsive

Daily reports

Once per day

Sufficient for reports

Error Handling

Always implement proper error handling:

[TTS Gateway Node]

[Catch Node]

[Function: Handle error]
    if (msg.error) {
      node.warn(`Gateway query failed: ${msg.error}`);
      
      // Check if it's an auth error
      if (msg.error.includes('401')) {
        node.error('API key invalid or expired');
      }
      
      // Fallback behavior
      msg.payload = {
        gateway_id: msg.gatewayId,
        connected: false,
        error: msg.error
      };
    }
    return msg;

[Dashboard with error state]

Caching

Reduce API calls by caching gateway data:

// Check cache first
const cacheKey = `gateway_${msg.gatewayId}`;
const cached = flow.get(cacheKey);
const cacheAge = flow.get(`${cacheKey}_time`);

if (cached && cacheAge && (Date.now() - cacheAge < 60000)) {
  // Use cached data (less than 1 minute old)
  msg.payload = cached;
  return msg;
}

// Otherwise query API and cache result
// (after TTS Gateway node)
flow.set(cacheKey, msg.payload);
flow.set(`${cacheKey}_time`, Date.now());

Status Persistence

Store gateway status in context for comparison:

// Store previous status
const gatewayId = msg.payload.gateway_id;
const previousStatus = flow.get(`status_${gatewayId}`);
const currentStatus = msg.payload.connected;

// Detect status changes
if (previousStatus !== currentStatus) {
  msg.statusChanged = true;
  msg.previousStatus = previousStatus;
}

// Update stored status
flow.set(`status_${gatewayId}`, currentStatus);

Troubleshooting

Common Issues

"Missing configuration" error

  • Ensure TTS Config node is properly configured

  • Verify Server URL and API Key are entered

  • Check API Key has required permissions

"Gateway ID not configured" error

  • Select a gateway ID in the node configuration (Configured mode)

  • Or ensure msg.payload contains gateway ID (From Message mode)

API Error: 401 Unauthorized

  • API Key is invalid or expired

  • Generate new API Key in TTS Console

  • Ensure API Key has RIGHT_GATEWAY_INFO and RIGHT_GATEWAY_STATUS_READ

API Error: 404 Not Found

  • Gateway ID is incorrect or doesn't exist

  • Verify gateway ID format (usually eui-...)

  • Check gateway exists in The Things Stack Console

Connection stats not available (null)

  • Gateway has never connected

  • Gateway is not currently active

  • Connection stats API returned 404 (expected for inactive gateways)

  • Gateway will show as connected: false

High latency or timeouts

  • TTS server might be slow or overloaded

  • Network connectivity issues

  • Try increasing polling interval


Performance & Limitations

API Calls

Each TTS Gateway node makes 2 API calls per execution:

  1. Gateway info (/api/v3/gateways/{gateway_id})

  2. Connection stats (/api/v3/gs/gateways/{gateway_id}/connection/stats)

Response Times

  • Typical: 1-3 seconds

  • Slow: 3-10 seconds (server load, network latency)

  • Timeout: 30 seconds

Rate Limits

The Things Stack has rate limits:

  • Community: Varies by server region

  • Enterprise: Configurable per deployment

Best Practices:

  • Don't poll faster than 1 minute intervals

  • Use caching for frequently accessed data

  • Batch queries when monitoring multiple gateways

  • Monitor for 429 (Too Many Requests) errors


Integration Examples

With Datacake Nodes

Combine gateway monitoring with device monitoring:

[Inject: Every 5 minutes]

[TTS Gateway Node]

[Function: Check gateway status]
    if (!msg.payload.connected) {
      return null; // Skip if gateway offline
    }
    return msg;

[Datacake GraphQL Semantic]
    Query devices with SIGNAL semantic

[Function: Analyze signal quality]

With InfluxDB

Store gateway metrics for historical analysis:

[TTS Gateway Node]

[Function: Format InfluxDB point]
    msg.payload = {
      measurement: 'gateway_stats',
      tags: {
        gateway_id: msg.payload.gateway_id,
        gateway_name: msg.payload.name
      },
      fields: {
        connected: msg.payload.connected ? 1 : 0,
        uplink_count: msg.payload.uplink_count || 0,
        downlink_count: msg.payload.downlink_count || 0,
        median_latency: parseInt(msg.payload.round_trip_times?.median || 0)
      },
      timestamp: new Date()
    };
    return msg;

[InfluxDB Out Node]

With Grafana Dashboard

Query InfluxDB for visualization:

SELECT 
  mean("uplink_count") as "Avg Uplinks",
  mean("median_latency") as "Avg Latency",
  sum("connected") as "Online Gateways"
FROM "gateway_stats"
WHERE time > now() - 24h
GROUP BY time(5m), "gateway_id"

Resources


API Reference

Gateway Info Endpoint

GET /api/v3/gateways/{gateway_id}
Authorization: Bearer {api_key}

Returns:

  • Gateway identification

  • Configuration details

  • Location information

  • Frequency plan

  • Antenna details

  • Version information

Connection Stats Endpoint

GET /api/v3/gs/gateways/{gateway_id}/connection/stats
Authorization: Bearer {api_key}

Returns:

  • Connection status

  • Connected/disconnected timestamps

  • Protocol in use

  • Message counters

  • Round-trip times

  • Sub-band utilization

Note: Returns 404 if gateway has never connected or connection stats are unavailable.


Appendix: Gateway ID Formats

Common Formats

  • EUI-based: eui-b827ebfffe8b1234

  • Custom ID: my-office-gateway

  • With dashes: office-gateway-01

Finding Your Gateway ID

  1. Log in to The Things Stack Console

  2. Navigate to Gateways

  3. Click on your gateway

  4. Copy the Gateway ID from the overview page

The gateway ID is shown at the top of the gateway details page and in the URL.

Last updated

Was this helpful?