Loading .vscode/settings.jsondeleted 100644 → 0 +0 −2 Original line number Diff line number Diff line { } No newline at end of file Dockerfile.ws-gatewaydeleted 100644 → 0 +0 −21 Original line number Diff line number Diff line # Use Node.js Alpine for smaller image size FROM node:18-alpine # Set working directory WORKDIR /app # Copy package files COPY package.json pnpm-lock.yaml ./ # Install pnpm and dependencies RUN npm install -g pnpm && \ pnpm install --prod --frozen-lockfile # Copy the WebSocket gateway script COPY ws-gateway.mjs ./ # Expose the WebSocket port EXPOSE 4001 # Run the WebSocket gateway CMD ["node", "ws-gateway.mjs"] seed1.jsdeleted 100644 → 0 +0 −24 Original line number Diff line number Diff line const bcrypt = require('bcryptjs'); async function generateEncryptedPasswords() { const users = [ { email: 'test1@test.com', password: 'password123', fullname: 'User 1' }, { email: 'admin@admin.com', password: 'adminpassword', fullname: 'Admin User' }, ]; for (const user of users) { try { const hashedPassword = await bcrypt.hash(user.password, 10); console.log(`INSERT INTO account (email, password, fullname) VALUES ( '${user.email}', '${hashedPassword}', '${user.fullname}' );`); } catch (error) { console.error(`Error hashing password for user ${user.email}:`, error); } } } generateEncryptedPasswords(); No newline at end of file ws-gateway.mjsdeleted 100644 → 0 +0 −105 Original line number Diff line number Diff line // ws-gateway.mjs import 'dotenv/config'; import { WebSocketServer } from 'ws'; import mqtt from 'mqtt'; // ---- Required envs (fail fast if missing) const required = ['MQTT_URL', 'MQTT_USER', 'MQTT_PASS']; for (const k of required) { if (!process.env[k] || String(process.env[k]).trim() === '') { console.error(`Missing env: ${k}`); process.exit(1); } } const { MQTT_URL, MQTT_USER, MQTT_PASS } = process.env; // ---- Topics const STATE_TOPIC = "depe_eventbus/grafeiokozani/out/ZWave_Node_010_MH8FC_Fan_Coil_Thermostat_Thermostat_mode/state"; const COMMAND_TOPIC = "depe_eventbus/grafeiokozani/in/ZWave_Node_010_MH8FC_Fan_Coil_Thermostat_Thermostat_mode/command"; // ---- MQTT connect //const MQTT_URL = MQTT_URL; console.log('Connecting to MQTT:', MQTT_URL); const client = mqtt.connect(MQTT_URL, { username: MQTT_USER, password: MQTT_PASS, rejectUnauthorized: false, reconnectPeriod: 3000, }); client.on('connect', (pkt) => { console.log('✅ MQTT connected', { sessionPresent: pkt?.sessionPresent }); client.subscribe(STATE_TOPIC, { qos: 1 }, (err, granted) => { if (err) return console.error('❌ MQTT subscribe error:', err); console.log('📡 Subscribed:', granted); }); }); client.on('reconnect', () => console.log('… MQTT reconnecting')); client.on('close', () => console.log('✖ MQTT connection closed')); client.on('offline', () => console.log('⚠ MQTT offline')); client.on('error', (e) => console.error('❌ MQTT error:', e?.message)); // ---- WS server const wss = new WebSocketServer({ port: 4001 }); console.log('WS listening ws://localhost:4001'); // ---- Cache last state so new WS clients get it immediately let lastState = null; // ---- Normalize device numeric state to friendly string function normalizeThermostatState(v) { const s = String(v ?? '').trim(); if (s === '0') return 'OFF'; if (s === '2') return 'COOL_ON'; return s || 'UNKNOWN'; } // ---- Forward MQTT state -> WS clients client.on("message", (topic, payloadBuf) => { if (topic !== STATE_TOPIC) return; const raw = payloadBuf.toString(); let parsed = null; try { parsed = JSON.parse(raw); } catch { /* payload might be plain text */ } const normalized = normalizeThermostatState(parsed?.state ?? raw); const out = { topic, raw, // original payload as string parsed, // parsed JSON if valid, otherwise null normalized, // friendly label (OFF / COOL_ON / UNKNOWN) ts: Date.now(), }; lastState = out; console.log("📩 state:", out); const msg = JSON.stringify(out); for (const ws of wss.clients) { if (ws.readyState === 1) ws.send(msg); } }); // ---- WS -> MQTT command publish wss.on("connection", (ws) => { // greet and send last known state ws.send(JSON.stringify({ type: "hello", msg: "connected to ws-gateway", ts: Date.now() })); if (lastState) ws.send(JSON.stringify(lastState)); ws.on("message", (raw) => { try { const { topic, message } = JSON.parse(raw.toString()); if (topic === COMMAND_TOPIC) { // Pass-through: device expects numeric strings "0" (OFF) or "2" (COOL) client.publish(COMMAND_TOPIC, String(message), { qos: 0, retain: false }, (err) => { if (err) console.error("❌ publish error:", err); else console.log("🚀 published command:", message); }); } } catch (err) { console.error("WS message error:", err); } }); }); Loading
.vscode/settings.jsondeleted 100644 → 0 +0 −2 Original line number Diff line number Diff line { } No newline at end of file
Dockerfile.ws-gatewaydeleted 100644 → 0 +0 −21 Original line number Diff line number Diff line # Use Node.js Alpine for smaller image size FROM node:18-alpine # Set working directory WORKDIR /app # Copy package files COPY package.json pnpm-lock.yaml ./ # Install pnpm and dependencies RUN npm install -g pnpm && \ pnpm install --prod --frozen-lockfile # Copy the WebSocket gateway script COPY ws-gateway.mjs ./ # Expose the WebSocket port EXPOSE 4001 # Run the WebSocket gateway CMD ["node", "ws-gateway.mjs"]
seed1.jsdeleted 100644 → 0 +0 −24 Original line number Diff line number Diff line const bcrypt = require('bcryptjs'); async function generateEncryptedPasswords() { const users = [ { email: 'test1@test.com', password: 'password123', fullname: 'User 1' }, { email: 'admin@admin.com', password: 'adminpassword', fullname: 'Admin User' }, ]; for (const user of users) { try { const hashedPassword = await bcrypt.hash(user.password, 10); console.log(`INSERT INTO account (email, password, fullname) VALUES ( '${user.email}', '${hashedPassword}', '${user.fullname}' );`); } catch (error) { console.error(`Error hashing password for user ${user.email}:`, error); } } } generateEncryptedPasswords(); No newline at end of file
ws-gateway.mjsdeleted 100644 → 0 +0 −105 Original line number Diff line number Diff line // ws-gateway.mjs import 'dotenv/config'; import { WebSocketServer } from 'ws'; import mqtt from 'mqtt'; // ---- Required envs (fail fast if missing) const required = ['MQTT_URL', 'MQTT_USER', 'MQTT_PASS']; for (const k of required) { if (!process.env[k] || String(process.env[k]).trim() === '') { console.error(`Missing env: ${k}`); process.exit(1); } } const { MQTT_URL, MQTT_USER, MQTT_PASS } = process.env; // ---- Topics const STATE_TOPIC = "depe_eventbus/grafeiokozani/out/ZWave_Node_010_MH8FC_Fan_Coil_Thermostat_Thermostat_mode/state"; const COMMAND_TOPIC = "depe_eventbus/grafeiokozani/in/ZWave_Node_010_MH8FC_Fan_Coil_Thermostat_Thermostat_mode/command"; // ---- MQTT connect //const MQTT_URL = MQTT_URL; console.log('Connecting to MQTT:', MQTT_URL); const client = mqtt.connect(MQTT_URL, { username: MQTT_USER, password: MQTT_PASS, rejectUnauthorized: false, reconnectPeriod: 3000, }); client.on('connect', (pkt) => { console.log('✅ MQTT connected', { sessionPresent: pkt?.sessionPresent }); client.subscribe(STATE_TOPIC, { qos: 1 }, (err, granted) => { if (err) return console.error('❌ MQTT subscribe error:', err); console.log('📡 Subscribed:', granted); }); }); client.on('reconnect', () => console.log('… MQTT reconnecting')); client.on('close', () => console.log('✖ MQTT connection closed')); client.on('offline', () => console.log('⚠ MQTT offline')); client.on('error', (e) => console.error('❌ MQTT error:', e?.message)); // ---- WS server const wss = new WebSocketServer({ port: 4001 }); console.log('WS listening ws://localhost:4001'); // ---- Cache last state so new WS clients get it immediately let lastState = null; // ---- Normalize device numeric state to friendly string function normalizeThermostatState(v) { const s = String(v ?? '').trim(); if (s === '0') return 'OFF'; if (s === '2') return 'COOL_ON'; return s || 'UNKNOWN'; } // ---- Forward MQTT state -> WS clients client.on("message", (topic, payloadBuf) => { if (topic !== STATE_TOPIC) return; const raw = payloadBuf.toString(); let parsed = null; try { parsed = JSON.parse(raw); } catch { /* payload might be plain text */ } const normalized = normalizeThermostatState(parsed?.state ?? raw); const out = { topic, raw, // original payload as string parsed, // parsed JSON if valid, otherwise null normalized, // friendly label (OFF / COOL_ON / UNKNOWN) ts: Date.now(), }; lastState = out; console.log("📩 state:", out); const msg = JSON.stringify(out); for (const ws of wss.clients) { if (ws.readyState === 1) ws.send(msg); } }); // ---- WS -> MQTT command publish wss.on("connection", (ws) => { // greet and send last known state ws.send(JSON.stringify({ type: "hello", msg: "connected to ws-gateway", ts: Date.now() })); if (lastState) ws.send(JSON.stringify(lastState)); ws.on("message", (raw) => { try { const { topic, message } = JSON.parse(raw.toString()); if (topic === COMMAND_TOPIC) { // Pass-through: device expects numeric strings "0" (OFF) or "2" (COOL) client.publish(COMMAND_TOPIC, String(message), { qos: 0, retain: false }, (err) => { if (err) console.error("❌ publish error:", err); else console.log("🚀 published command:", message); }); } } catch (err) { console.error("WS message error:", err); } }); });