help Help

IoT Files Reference

Complete documentation for IoT integration scripts
Web portal, sensors, relays, and audio playback

[ Node.js | GPIO | SignService API ]

IoT Directory Structure

The IoT module contains several scripts for bidirectional communication between Raspberry Pi hardware and SignPlayer. Each file serves a specific purpose in the IoT ecosystem.

iot/ |-- iotWebPortal.js # Web-based testing portal |-- iotSendTemperture.js # Temperature sensor to SignPlayer |-- iotReceiveAlertRelay.js # SignPlayer events to relay control |-- iotReceiveAlertAudio.js # SignPlayer events to audio playback |-- iotSendCurl.sh # Example curl command |-- version.txt # Version tracking |-- readme/ # Documentation folder |-- README.md # Main documentation |-- ARCHITECTURE.md # System architecture |-- FILES.md # File reference |-- API.md # API reference |-- QUICK-START.md # Quick start guide
Portal

iotWebPortal.js

iot/iotWebPortal.js

Purpose

A web-based developer portal for testing and debugging IoT events. Provides a visual interface to send events to SignPlayer, register event listeners, view received events in real-time, and configure SignService connection.

Usage

# Start the web portal
node iotWebPortal.js
# Opens browser automatically to http://localhost:5000

Architecture

+-----------------------------------------------------------------------------+
|                          IoT Web Portal                                      |
|                          Port 5000                                           |
+-----------------------------------------------------------------------------+
|                                                                             |
|   +---------------------------+   +---------------------------+             |
|   |     HTTP Server           |   |     SSE Event Stream      |             |
|   |   - Serves HTML UI        |   |   - Real-time updates     |             |
|   |   - REST endpoints        |   |   - Client notifications  |             |
|   +---------------------------+   +---------------------------+             |
|                                                                             |
|   +---------------------------+   +---------------------------+             |
|   |     SignService Client    |   |     Callback Server       |             |
|   |   - Send events           |   |   - Receive callbacks     |             |
|   |   - Register listeners    |   |   - Forward to UI         |             |
|   +---------------------------+   +---------------------------+             |
|                                                                             |
+-----------------------------------------------------------------------------+

Key Features

Feature Description
Send to Player Send JSON events directly to SignPlayer
Event Listeners Register callbacks for specific events
Real-time Display See received events instantly via SSE
IP Detection Auto-detects local IP for callback URL
SignService Config Configure which SignService to connect to

REST Endpoints

Endpoint Method Description
/ GET Serves HTML UI
/events GET SSE stream for real-time updates
/callback POST Receives events from SignService
/register POST Register event listener
/unregister POST Unregister event listener
/send POST Send event to SignPlayer
/registeredEvents GET List registered events
/setSignServiceHost POST Configure SignService IP
/getCallbackHost GET Get this device's IP

Configuration

Parameter Default Description
PORT 5000 Web portal port
SIGNSERVICE_PORT 8094 SignService HTTPS port
SIGNSERVICE_HOST localhost Can be changed via UI

Code Walkthrough

1. Auto-detect Local IP
function getLocalIP() {
    const interfaces = os.networkInterfaces();
    // Skip virtual adapters (VMware, VirtualBox, Docker, etc.)
    const skipNames = ['vmware', 'virtualbox', 'vbox', 'docker', 'vethernet', 'hyper-v'];
    const skipRanges = ['192.168.56.', '192.168.45.', '172.17.', '172.18.', '10.0.2.'];
    // Prefer common LAN ranges
    const preferredRanges = ['192.168.68.', '192.168.1.', '192.168.0.', '10.0.0.', '10.0.1.'];
    // ... returns best IP address
}
2. Register Event with SignService
function registerEventWithSignService(eventName) {
    const callbackUrl = encodeURIComponent(`http://${CALLBACK_HOST}:${PORT}/callback`);
    const path = `/player/registerEvent?eventName=${eventName}&callbackUrl=${callbackUrl}`;
    // POST to SignService HTTPS endpoint
}
3. Send Event to SignPlayer
function sendEventToPlayer(eventName, jsonData) {
    const path = `/player/sendEvent?eventName=${eventName}&store=1`;
    // POST JSON data to SignService
}
4. SSE Broadcasting
function broadcastToClients(data) {
    const message = `data: ${JSON.stringify(data)}\n\n`;
    sseClients.forEach(client => {
        client.write(message);
    });
}
Sender

iotSendTemperture.js

iot/iotSendTemperture.js

Purpose

Reads temperature from a DHT11 sensor and sends it to SignPlayer every 4 seconds. Includes a demo mode for testing without hardware.

Usage

# Demo mode (simulated temperature)
node iotSendTemperture.js

# Live mode (real DHT11 sensor)
# Edit file: change DEMO = false
node iotSendTemperture.js

Architecture

+-----------------------------------------------------------------------------+
|                       Temperature Sender Flow                                |
+-----------------------------------------------------------------------------+

  +----------+      +-------------+      +-------------+      +-------------+
  |  DHT11   |      |  Node.js    |      | SignService |      | SignPlayer  |
  |  Sensor  | ---> |  Script     | ---> |   (C#)      | ---> |  Display    |
  +----------+      +-------------+      +-------------+      +-------------+
      |                   |                    |                    |
      |  GPIO 5           |                    |                    |
      |  Read             |                    |                    |
      |  Every 4s         |                    |                    |
      |                   |  POST JSON:        |                    |
      |                   |  {"name": "25.3"}  |                    |
      |                   |                    |                    |

Configuration

Parameter Default Description
DEMO true true = simulated, false = real sensor
SIGNSERVICE_HOST localhost Windows PC IP running SignService
SIGNSERVICE_PORT 8094 SignService HTTPS port
EVENT_NAME myCustomEvent Must match SignStudio config
POLL_INTERVAL 4 Seconds between readings
SENSOR_PIN 5 GPIO pin for DHT11 data

Key Functions

Send Temperature
function sendTemperature(temp_c, temp_f, humidity) {
    const data = JSON.stringify({
        name: varied_c + ' degrees'
    });

    // Direct HTTPS POST to SignService
    const curlCmd = `curl '${IOT_SERVER}' \
      -X POST \
      -H 'Content-Type: application/json' \
      --data-raw '${data}' \
      --insecure \
      --silent`;

    exec(curlCmd, (error, stdout, stderr) => {
        // Handle response
    });
}
Add Variance (for realistic readings)
function addVariance(value) {
    const variance = (Math.random() - 0.5) * 0.6; // -0.3 to +0.3
    return Math.round((value + variance) * 10) / 10;
}

SignStudio Configuration

To display the temperature in SignPlayer, create a Label component with data binding:

{
  "Label": [{
    "Text": ["Temperature"],
    "DataBinding": {
      "eventName": "myCustomEvent",
      "field": "name"
    }
  }]
}
Receiver

iotReceiveAlertRelay.js

iot/iotReceiveAlertRelay.js

Purpose

Listens for events from SignPlayer and controls a relay on GPIO 4. When a user touches a button in SignPlayer, this script receives the event and turns the relay ON or OFF.

Usage

# Default event name: clickAlarm
node iotReceiveAlertRelay.js

# Custom event name
node iotReceiveAlertRelay.js myEventName

Architecture

+-----------------------------------------------------------------------------+
|                         Relay Control Flow                                   |
+-----------------------------------------------------------------------------+

  +-------------+      +-------------+      +-------------+      +----------+
  | SignPlayer  |      | SignService |      |  Node.js    |      |  Relay   |
  | (Touch)     | ---> |   (C#)      | ---> |  Script     | ---> |  GPIO 4  |
  +-------------+      +-------------+      +-------------+      +----------+
       |                     |                    |                   |
       | User taps           |                    |                   |
       | "Alarm ON"          |                    |                   |
       | button              |                    |                   |
       |                     | HTTP Callback:     |                   |
       |                     | {"data": {         |                   |
       |                     |   "clickAlarm":    |                   |
       |                     |   "on"}}           |                   |
       |                     |                    | GPIO Write:       |
       |                     |                    | Pin 4 = HIGH      |
       |                     |                    |                   |

Configuration

Parameter Default Description
RELAY_PIN 4 GPIO pin for relay (Pin 7 on header)
PORT 5001 Callback server port
SIGNSERVICE_HOST localhost SignService IP address
SIGNSERVICE_PORT 8094 SignService HTTPS port
DEFAULT_EVENT_NAME clickAlarm Event to listen for
REGISTER_INTERVAL 5 Re-register every N seconds

Key Functions

Initialize GPIO
function initGpio() {
    const cmd = `python3 -c "import lgpio; h=lgpio.gpiochip_open(0); lgpio.gpio_claim_output(h, ${RELAY_PIN}); lgpio.gpio_write(h, ${RELAY_PIN}, 0); lgpio.gpiochip_close(h)"`;
    exec(cmd, (err) => {
        if (!err) gpioInitialized = true;
    });
}
Control Relay
function setRelay(on) {
    const value = on ? 1 : 0;
    const cmd = `python3 -c "import lgpio; h=lgpio.gpiochip_open(0); lgpio.gpio_claim_output(h, ${RELAY_PIN}); lgpio.gpio_write(h, ${RELAY_PIN}, ${value}); lgpio.gpiochip_close(h)"`;
    exec(cmd, (err) => {
        console.log(`>>> RELAY ${on ? 'ON' : 'OFF'}`);
    });
}
Process Callback
// In HTTP server callback handler:
if (json.data && json.data.clickAlarm) {
    const alarmState = json.data.clickAlarm.toLowerCase();
    if (alarmState.includes('on')) {
        setRelay(true);
    } else if (alarmState.includes('off')) {
        setRelay(false);
    }
}

Hardware Wiring

+-------------+     +---------------------+
| Relay       |     |    Raspberry Pi     |
| Module      |     |                     |
|-------------|     |---------------------|
| VCC (red)   |---->| Pin 2 (5V)          |
| IN (yellow) |---->| Pin 7 (GPIO 4)      |
| GND (black) |---->| Pin 6 (GND)         |
+-------------+     +---------------------+

SignStudio Configuration

Create a Button component with EventCommands:

{
  "$": {
    "player": "7000",
    "label": "Alarm Button"
  },
  "Data": [{
    "Button": [{
      "$": {
        "label": "Alarm ON",
        "fieldName": "alarmButton"
      }
    }],
    "EventCommands": [{
      "EventCommand": [{
        "$": {
          "from": "click",
          "command": "sendEvent"
        },
        "Params": {
          "eventName": "clickAlarm",
          "data": {
            "clickAlarm": "on"
          }
        }
      }]
    }]
  }]
}
Receiver

iotReceiveAlertAudio.js

iot/iotReceiveAlertAudio.js

Purpose

Similar to iotReceiveAlertRelay.js, but plays audio instead of controlling a relay. Uses the speaker.py Python script to play alarm sounds.

Usage

# Default event name: clickAlarm
node iotReceiveAlertAudio.js

# Custom event name
node iotReceiveAlertAudio.js myEventName

Architecture

+-----------------------------------------------------------------------------+
|                         Audio Control Flow                                   |
+-----------------------------------------------------------------------------+

  +-------------+      +-------------+      +-------------+      +----------+
  | SignPlayer  |      | SignService |      |  Node.js    |      | Speaker  |
  | (Touch)     | ---> |   (C#)      | ---> |  Script     | ---> | (Audio)  |
  +-------------+      +-------------+      +-------------+      +----------+
       |                     |                    |                   |
       | User taps           |                    |                   |
       | "Alarm ON"          |                    |                   |
       | button              |                    |                   |
       |                     | HTTP Callback:     |                   |
       |                     | {"data": {         |                   |
       |                     |   "clickAlarm":    |                   |
       |                     |   "on"}}           |                   |
       |                     |                    | Play sound:       |
       |                     |                    | speaker.py        |
       |                     |                    | gas_alarm         |
       |                     |                    |                   |

Configuration

Parameter Default Description
SPEAKER_SCRIPT /root/_node-dev/pi/lib/speaker.py Path to audio playback script
ALARM_TYPE gas_alarm Type of alarm sound to play
PORT 5002 Different port from relay version
DEBOUNCE_MS 500 Ignore rapid duplicate calls

Key Functions

Play Alarm
function playAlarm() {
    const now = Date.now();
    if (now - lastPlayTime < DEBOUNCE_MS) return;  // Debounce
    if (isPlaying) return;  // Already playing

    isPlaying = true;
    const cmd = `python3 ${SPEAKER_SCRIPT} ${ALARM_TYPE}`;
    exec(cmd, (err, stdout, stderr) => {
        isPlaying = false;
    });
}
Stop Alarm
function stopAlarm() {
    exec('pkill -f "speaker.py" 2>/dev/null', () => {
        isPlaying = false;
    });
}

Relay vs Audio Comparison

Feature iotReceiveAlertRelay.js iotReceiveAlertAudio.js
Port 5001 5002
Output GPIO Relay Audio Speaker
Control setRelay(true/false) playAlarm()/stopAlarm()
Debounce No Yes (500ms)
Hardware Relay Module Speaker/Audio Hat
Example

iotSendCurl.sh

iot/iotSendCurl.sh

Purpose

Example curl command for testing event sending from the command line. Useful for debugging and understanding the API.

Contents

curl 'http://localhost:5000/send?eventName=myCustomEvent' \
  -H 'Accept: */*' \
  -H 'Content-Type: application/json' \
  --data-raw 

    
    
    
    
    

live examples, see it in action

Checkout live examples of Digital Signage presentations. The SignPlayer can run everywhere, on Android, Windows, Mac, iPad and even inside your web browser

View More

product pricing

Find out why people call us “the world’s most popular digital signage platform”

View More

customers

Can it really be free?
What’s the catch?
Want to be amazed?
Read on...

View More
{\n "name": "hello2"\n}'

Usage

# Make executable
chmod +x iotSendCurl.sh

# Run
./iotSendCurl.sh

Direct SignService curl

To send directly to SignService (bypassing the web portal):

curl 'https://192.168.68.52:8094/player/sendEvent?eventName=myCustomEvent&store=1' \
  -X POST \
  -H 'Content-Type: application/json' \
  --data-raw '{"name": "hello"}' \
  --insecure

Common Patterns

Event Data Structure

Events sent to SignPlayer follow this structure:

{
  "name": "value",           // Simple value for Label binding
  "fieldName": "value",      // Field-specific value
  "nested": {
    "data": "value"          // Nested data structure
  }
}

Callback Data Structure

Callbacks received from SignService:

{
  "eventName": "clickAlarm",
  "timestamp": "2025-12-26T10:30:00Z",
  "data": {
    "clickAlarm": "on"
  }
}

Error Handling Pattern

try {
    await someOperation();
} catch (e) {
    console.error(`Failed: ${e.message}`);
    // Don't crash - continue running
}

process.on('uncaughtException', (err) => {
    console.error('Uncaught exception:', err.message);
    // Log but don't exit
});

Cleanup on Exit

process.on('SIGINT', async () => {
    console.log('\n\nShutting down...');

    // Unregister from SignService
    try {
        await unregisterEventWithSignService(EVENT_NAME);
    } catch (e) {
        // Ignore errors on shutdown
    }

    // Turn off outputs
    setRelay(false);  // or stopAlarm();

    console.log(`Total events received: ${eventCount}`);
    process.exit(0);
});

live examples, see it in action

Checkout live examples of Digital Signage presentations. The SignPlayer can run everywhere, on Android, Windows, Mac, iPad and even inside your web browser

View More

product pricing

Find out why people call us “the world’s most popular digital signage platform”

View More

customers

Can it really be free?
What’s the catch?
Want to be amazed?
Read on...

View More