> For the complete documentation index, see [llms.txt](https://docs.datacake.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.datacake.de/cake-red/datacake-nodes/datacake-lns-nodes.md).

# Datacake LNS Nodes

### 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. Log in to [The Things Stack Console](https://console.cloud.thethings.network/)
2. Go to your application or user settings
3. Navigate to **API Keys**
4. Click **Add API Key**
5. **Required Rights:**
   * `RIGHT_GATEWAY_INFO` - Read gateway information
   * `RIGHT_GATEWAY_STATUS_READ` - Read gateway status and statistics
6. Generate and copy the API key
7. 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

```json
{
  "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: "network-admin@company.com",
      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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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
* Check [TTS Status Page](https://status.thethingsnetwork.org)

***

### 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:

```sql
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

* **The Things Stack Documentation:** [thethingsindustries.com/docs](https://www.thethingsindustries.com/docs/)
* **TTS API Reference:** [thethingsindustries.com/docs/reference/api](https://www.thethingsindustries.com/docs/reference/api/)
* **The Things Network Forum:** [thethingsnetwork.org/forum](https://www.thethingsnetwork.org/forum/)
* **TTS Status Page:** [status.thethingsnetwork.org](https://status.thethingsnetwork.org/)

***

### 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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.datacake.de/cake-red/datacake-nodes/datacake-lns-nodes.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
