From 1f3292bc173bf108910b38bbcd2b666ce0d58aad Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Wed, 24 Dec 2025 23:38:36 +0100 Subject: [PATCH] ex --- agents/wss-client-example/example.js | 53 ++++++++++ agents/wss-client-example/lib.js | 130 +++++++++++++++++++++++++ agents/wss-client-example/package.json | 13 +++ 3 files changed, 196 insertions(+) create mode 100644 agents/wss-client-example/example.js create mode 100644 agents/wss-client-example/lib.js create mode 100644 agents/wss-client-example/package.json diff --git a/agents/wss-client-example/example.js b/agents/wss-client-example/example.js new file mode 100644 index 0000000..45243cb --- /dev/null +++ b/agents/wss-client-example/example.js @@ -0,0 +1,53 @@ +import { TischlerClient } from './lib.js'; + +// Configuration (Replace with your actual values or set ENV vars) +// Example: SERVER_URL=ws://localhost:3000 API_KEY=k_... node example.js +const SERVER_URL = process.env.SERVER_URL || 'wss://dash.bosewolf.de/agentapi/'; +const API_KEY = process.env.API_KEY || 'YOUR_API_KEY_HERE'; + +if (API_KEY === 'YOUR_API_KEY_HERE') { + console.error('Please set API_KEY environment variable or edit example.js'); + process.exit(1); +} + +const client = new TischlerClient(SERVER_URL, API_KEY); + +client.onAuthenticated = () => { + // Determine random values for demo + const temp = 20 + Math.random() * 5; + const humidity = 40 + Math.random() * 20; + + // Example 1: Numeric Data (e.g. Temperature) + // Note: The 'device' id will be prefixed by the server with your Agent's prefix. + const readings = [ + { + device: 'sensor-1', + channel: 'temperature', + value: temp + }, + { + device: 'sensor-1', + channel: 'humidity', + value: humidity + }, + // Example 2: Generic JSON Data (e.g. Status object) + { + device: 'sensor-1', + channel: 'status', + data: { status: 'ok', battery: '95%', fw: '1.2.0' } // 'data' field for JSON + } + ]; + + client.sendReadings(readings); + + // Close after a standardized delay to ensure ACK is received + setTimeout(() => { + console.log('Done. Closing connection.'); + client.close(); + }, 2000); +}; + +// Start +client.connect().catch(err => { + console.error('Failed to connect:', err); +}); diff --git a/agents/wss-client-example/lib.js b/agents/wss-client-example/lib.js new file mode 100644 index 0000000..fd759d3 --- /dev/null +++ b/agents/wss-client-example/lib.js @@ -0,0 +1,130 @@ +import WebSocket from 'ws'; + +/** + * A simple client for the TischlerCtrl WebSocket API. + */ +export class TischlerClient { + /** + * @param {string} url - The WebSocket server URL (e.g., 'ws://localhost:3000') + * @param {string} apiKey - Your Agent API Key + */ + constructor(url, apiKey) { + this.url = url; + this.apiKey = apiKey; + this.ws = null; + this.authenticated = false; + this.onAuthenticated = null; // Callback + } + + /** + * Connect to the WebSocket server. + * @returns {Promise} Resolves when connected (but not yet authenticated) + */ + connect() { + return new Promise((resolve, reject) => { + this.ws = new WebSocket(this.url); + + this.ws.on('open', () => { + console.log('[Client] Connected to server.'); + this.authenticate(); + resolve(); + }); + + this.ws.on('message', (data) => this.handleMessage(data)); + + this.ws.on('error', (err) => { + console.error('[Client] Connection error:', err.message); + reject(err); + }); + + this.ws.on('close', () => { + console.log('[Client] Disconnected.'); + this.authenticated = false; + }); + }); + } + + /** + * Send authentication message. + */ + authenticate() { + console.log('[Client] Authenticating...'); + this.send({ + type: 'auth', + apiKey: this.apiKey + }); + } + + /** + * Send sensor readings. + * @param {Array} readings - Array of reading objects + * @example + * client.sendReadings([ + * { device: 'temp-sensor-1', channel: 'temp', value: 24.5 }, + * { device: 'temp-sensor-1', channel: 'config', data: { mode: 'eco' } } + * ]); + */ + sendReadings(readings) { + if (!this.authenticated) { + console.warn('[Client] Cannot send data: Not authenticated.'); + return; + } + + console.log(`[Client] Sending ${readings.length} readings...`); + this.send({ + type: 'data', + readings: readings + }); + } + + /** + * Handle incoming messages. + */ + handleMessage(data) { + try { + const message = JSON.parse(data.toString()); + + switch (message.type) { + case 'auth': + if (message.success) { + this.authenticated = true; + console.log(`[Client] Authenticated as "${message.name}" (Prefix: ${message.devicePrefix})`); + if (this.onAuthenticated) this.onAuthenticated(); + } else { + console.error('[Client] Authentication failed:', message.error); + this.ws.close(); + } + break; + + case 'ack': + console.log(`[Client] Server acknowledged ${message.count} readings.`); + break; + + case 'error': + console.error('[Client] Server error:', message.error); + break; + + default: + console.log('[Client] Received:', message); + } + } catch (err) { + console.error('[Client] Failed to parse message:', err); + } + } + + /** + * Helper to send JSON object. + */ + send(obj) { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(obj)); + } + } + + /** + * Close connection. + */ + close() { + if (this.ws) this.ws.close(); + } +} diff --git a/agents/wss-client-example/package.json b/agents/wss-client-example/package.json new file mode 100644 index 0000000..ef2084d --- /dev/null +++ b/agents/wss-client-example/package.json @@ -0,0 +1,13 @@ +{ + "name": "wss-client-example", + "version": "1.0.0", + "description": "Example client for TischlerCtrl WebSocket API", + "main": "lib.js", + "type": "module", + "scripts": { + "start": "node example.js" + }, + "dependencies": { + "ws": "^8.18.0" + } +} \ No newline at end of file