UNPKG

smithtek-nodered-sms

Version:

A Node-RED node for sending SMS via ZTE USB modem.

221 lines (191 loc) 9.35 kB
// zte-sms.js - Main Node-RED Node const fetch = require("node-fetch"); module.exports = function(RED) { function ZteSmsNode(config) { RED.nodes.createNode(this, config); let node = this; // Configurable properties node.phone = config.phone || ""; node.password = config.password || "Admin"; node.modemIp = config.modemIp || "192.168.0.1"; node.smsInterval = config.smsInterval || 10; // Default SMS interval in seconds // Start periodic polling startPolling(node); node.on('input', async function(msg) { let phoneNumber = node.phone || msg.phone; let password = node.password; let modemIp = node.modemIp; let message = msg.payload; if (!phoneNumber || !message) { node.error("Phone number and message are required.", msg); return; } try { await loginToModem(password, modemIp); let response = await sendSms(phoneNumber, message, modemIp); msg.payload = response; node.send([null, null, msg]); // Only send to the 3rd output (status pin) } catch (error) { node.error(error, msg); } }); } function startPolling(node) { if (node.smsIntervalId) clearInterval(node.smsIntervalId); if (node.modemIntervalId) clearInterval(node.modemIntervalId); // SMS Polling (User-configurable) node.smsIntervalId = setInterval(async () => { try { let lastSms = await getLastSms(node.modemIp); if (lastSms.message === "No message content") { node.warn("No new messages found. Triggering login."); await loginToModem(node.password, node.modemIp); // Trigger login process } else { // Only forward if there is a valid message node.send([ { payload: { from: lastSms.from || "Unknown", time: lastSms.time || "Unknown" } }, // Pin 1 (Sender + Time) { payload: lastSms.message }, // Pin 2 (Message) null // Pin 3 (Not used in this part) ]); } } catch (error) { node.warn("Failed to fetch SMS: " + error); } }, node.smsInterval * 1000); // Modem Status Polling (Fixed at 10s) node.modemIntervalId = setInterval(async () => { try { let modemStatus = await getModemStatus(node.modemIp); if (modemStatus) { node.send([null, null, { payload: modemStatus }]); // Pin 3 (Modem Status) } } catch (error) { node.warn("Failed to fetch modem status: " + error); } }, 10000); } async function loginToModem(password, modemIp) { let encodedPassword = Buffer.from(password).toString('base64'); let loginPayload = `isTest=false&goformId=LOGIN&password=${encodedPassword}`; await fetch(`http://${modemIp}/goform/goform_set_cmd_process`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Referer": `http://${modemIp}/index.html` }, body: loginPayload }); } async function sendSms(number, text, modemIp) { function textToHex(str) { return Array.from(str).map(c => ('000' + c.charCodeAt(0).toString(16)).slice(-4)).join(''); } function getSmsTime() { let now = new Date(); return `${now.getFullYear().toString().substr(-2)};${('0' + (now.getMonth() + 1)).slice(-2)};${('0' + now.getDate()).slice(-2)};${('0' + now.getHours()).slice(-2)};${('0' + now.getMinutes()).slice(-2)};${('0' + now.getSeconds()).slice(-2)};+8`; } let payload = `isTest=false&goformId=SEND_SMS&notCallback=true&Number=${encodeURIComponent(number)}&sms_time=${encodeURIComponent(getSmsTime())}&MessageBody=${textToHex(text)}&ID=-1&encode_type=UNICODE`; let response = await fetch(`http://${modemIp}/goform/goform_set_cmd_process`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Referer": `http://${modemIp}/index.html` }, body: payload }); return await response.text(); } async function getLastSms(modemIp) { try { let response = await fetch(`http://${modemIp}/goform/goform_get_cmd_process?isTest=false&cmd=sms_data_total&page=0&data_per_page=500&mem_store=1&tags=10&order_by=order+by+id+desc`, { method: "GET", headers: { "Referer": `http://${modemIp}/index.html`, "Accept": "application/json, text/javascript, */*; q=0.01" } }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status}`); } let data = await response.json(); // Check if we received a valid messages array if (!data || typeof data !== "object" || !Array.isArray(data.messages) || data.messages.length === 0) { console.warn("No received messages found."); return { from: "Unknown", time: "Unknown", message: "No message content" }; } // Filter messages to only include received ones (tag: 1) let receivedMessages = data.messages.filter(msg => msg.tag === "1"); if (receivedMessages.length === 0) { console.warn("No received messages found."); return { from: "Unknown", time: "Unknown", message: "No received messages found" }; } // Get the most recent received SMS let lastSms = receivedMessages[0]; function decodeUCS2(hex) { let str = ''; for (let i = 0; i < hex.length; i += 4) { let charCode = parseInt(hex.substr(i, 4), 16); if (!isNaN(charCode)) { str += String.fromCharCode(charCode); } } return str; } return { from: lastSms.number || "Unknown", time: lastSms.date || "Unknown", message: lastSms.content ? decodeUCS2(lastSms.content) : "No message content" }; } catch (error) { console.error("Error fetching last SMS:", error); return { from: "Error", time: "Error", message: "Failed to fetch SMS" }; } } async function getModemStatus(modemIp) { try { let response = await fetch(`http://${modemIp}/goform/goform_get_cmd_process?multi_data=1&isTest=false&sms_received_flag_flag=0&sts_received_flag_flag=0&cmd=network_type,network_provider,ppp_status,realtime_tx_bytes,realtime_rx_bytes,opms_wan_auto_mode,signalbar,battery_temp,lan_ipaddr,roam_setting_option&_=` + Date.now(), { method: "GET", headers: { "Referer": `http://${modemIp}/index.html`, "Accept": "application/json, text/javascript, */*; q=0.01", "X-Requested-With": "XMLHttpRequest" } }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status}`); } let data = await response.json(); return { network_type: data.network_type || "Unknown", network_provider: data.network_provider || "Unknown", ppp_status: data.ppp_status || "Unknown", realtime_tx_bytes: data.realtime_tx_bytes || "0", realtime_rx_bytes: data.realtime_rx_bytes || "0", opms_wan_auto_mode: data.opms_wan_auto_mode || "Unknown", signalbar: data.signalbar || "0", battery_temp: data.battery_temp || "Unknown", lan_ipaddr: data.lan_ipaddr || "Unknown", roam_setting_option: data.roam_setting_option || "Unknown" }; } catch (error) { console.error("Error fetching modem status:", error); return { network_type: "Error", network_provider: "Error", ppp_status: "Error" }; } } RED.nodes.registerType("zte-sms", ZteSmsNode, { defaults: { phone: { value: "" }, password: { value: "Admin" }, modemIp: { value: "192.168.0.1" }, smsInterval: { value: 10 } }, inputs: 1, outputs: 3, icon: "font-awesome/fa-comment", label: function() { return "ZTE SMS"; } }); };