serialport-gsm-ts
v1.0.0
Published
Serialport implementation for GSM commands (send text/flash messages, read, etc.)
Maintainers
Keywords
Readme
serialport-gsm-ts
Type-safe GSM modem library for Node.js built on top of serialport.
It provides a high-level API for:
- opening and initializing GSM modems
- switching modem mode (
PDU/SMS) - sending SMS
- reading SIM inbox messages
- receiving SMS, incoming-call, and USSD events
- executing custom AT commands
This package is focused primarily on PDU mode and real serial-connected GSM modem workflows.
Compatibility
- Runtime support: Node.js only
- Module support: ESM + CommonJS
- Bun: not supported / not recommended for serial modem access
Highlights
- TypeScript-first source
- Strict typings
- Promise and callback support
- CommonJS and ESM package output
- Event-driven incoming SMS / call handling
- Queue-based AT command execution
- Works well in Node.js
Runtime Recommendation
Use Node.js for modem access.
serialport is a native/hardware-facing dependency and Node is the supported runtime path. Bun may work in some cases, but for real modem access this package should be treated as Node-first.
Installation
npm install serialport-gsm-tsPackage Formats
This package ships:
- CommonJS:
require("serialport-gsm-ts") - ESM:
import serialportgsm from "serialport-gsm-ts" - Types:
dist/index.d.ts
Quick Start
TypeScript / ESM
import serialportgsm from "serialport-gsm-ts";
const modem = serialportgsm.Modem();
await modem.open("COM15", {
baudRate: 9600,
dataBits: 8,
parity: "none",
stopBits: 1,
rtscts: false,
xon: false,
xoff: false,
xany: false,
incomingCallIndication: true,
incomingSMSIndication: true,
logger: console
});
await modem.initializeModem();
await modem.setModemMode(undefined, true, 30000, "PDU");
const signal = await modem.getNetworkSignal();
console.log(signal);CommonJS / Node
const serialportgsm = require("serialport-gsm-ts");
const modem = serialportgsm.Modem();
modem.open("COM15", {
baudRate: 9600,
dataBits: 8,
parity: "none",
stopBits: 1,
rtscts: false,
incomingCallIndication: true,
incomingSMSIndication: true,
logger: console
}, (error) => {
if (error) {
console.error("Failed to open modem", error);
}
});
modem.on("open", () => {
modem.initializeModem((result, error) => {
if (error) {
console.error("Failed to initialize modem", error);
return;
}
console.log("Initialized", result);
});
});Full Example
A full TypeScript hardware example is available at:
Run it with:
npm run exampleThat example includes:
- fixed modem constants
- listeners for all public events
- basic modem info queries
- SIM inbox reading
- custom command examples
- optional SMS / USSD / phonebook / delete operations behind toggles
API Overview
Top-Level API
serialportgsm.list()
Lists available serial ports.
Promise:
const ports = await serialportgsm.list();Callback:
serialportgsm.list((error, ports) => {
if (error) {
console.error(error);
return;
}
console.log(ports);
});serialportgsm.Modem()
Creates a modem instance.
const modem = serialportgsm.Modem();serialportgsm.serialport
Direct access to the underlying serialport package export.
const serialport = serialportgsm.serialport;Open Options
Serial options
| Option | Type | Notes |
| --- | --- | --- |
| baudRate | number | Common values: 9600, 19200, 115200 |
| dataBits | 5 \| 6 \| 7 \| 8 | Usually 8 |
| stopBits | 1 \| 2 | Usually 1 |
| parity | "none" \| "even" \| "odd" \| "mark" \| "space" | Usually "none" |
| rtscts | boolean | Hardware flow control |
| xon | boolean | Software flow control |
| xoff | boolean | Software flow control |
| xany | boolean | Software flow control |
Library-specific options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| autoDeleteOnReceive | boolean | false | Auto-delete incoming SMS after reading |
| enableConcatenation | boolean | false | Merge multipart SMS into one message |
| incomingCallIndication | boolean | false | Emit onNewIncomingCall events |
| incomingSMSIndication | boolean | true | Emit SMS indications |
| incomingSMSIndicationDelay | number | 10000 | Delay for multipart SMS assembly |
| pin | string | "" | SIM PIN used during initialization |
| customInitCommand | string | "" | Extra modem-specific init command |
| cnmiCommand | string | "AT+CNMI=2,1,0,2,1" | SMS indication configuration |
| logger | { debug(message: string): void } | no-op | Enable modem read/write logging |
Modem Methods
All modem methods support:
- callback style
- promise style
Examples below use the promise form for brevity.
Connection and setup
open(device, options)close()initializeModem()resetModem()enableEcho()checkModem()setModemMode(callback?, priority?, timeout?, mode?)
Example:
await modem.open("COM15", options);
await modem.initializeModem();
await modem.setModemMode(undefined, true, 30000, "PDU");PIN and SIM
checkPINRequired()providePIN(pin)checkSimMemory()getSimInbox()readSMSById(id)deleteMessage(message)deleteSimMessages(id)deleteAllSimMessages()
Device info
getModemSerial()getNetworkSignal()getOwnNumber()
Important:
getOwnNumber() depends on modem/SIM support for AT+CNUM. Some devices return nothing even when SMS works correctly.
Phonebook
selectPhonebookStorage(memory)writeToPhonebook(number, name)setOwnNumber(number, callback?, name?)
Messaging
sendSMS(number, message, alert?)
alert = true sends a class-0 / flash message when supported by the modem/network.
Calls and notifications
enableCLIP()enableCNMI()hangupCall()
Note:
Incoming-call rejection can vary by modem and carrier. The current implementation prefers AT+CHUP and falls back to ATH for better real-world behavior.
USSD
sendUSSD(command)
Example:
if (modem.sendUSSD) {
const result = await modem.sendUSSD("*123#");
console.log(result);
}Custom AT commands
executeCommand(command, callback?, priority?, timeout?)
Simple one-line example:
const result = await modem.executeCommand("AT^GETPORTMODE");
console.log(result);Multi-line custom parser example:
const result = await new Promise((resolve, reject) => {
const item = modem.executeCommand("AT^SETPORT=?", (data, error) => {
if (error) {
reject(error);
return;
}
resolve(data);
});
if (!("logic" in item)) {
reject(new Error("Failed to create parser"));
return;
}
const parsed = {};
item.logic = (line) => {
if (line.startsWith("^SETPORT:")) {
const parts = line.split(":");
parsed[parts[1]] = parts[2]?.trim();
return;
}
if (line.includes("OK")) {
return {
resultData: {
status: "success",
request: "executeCommand",
data: { result: parsed }
},
returnResult: true
};
}
if (line.includes("ERROR")) {
return {
resultData: {
status: "ERROR",
request: "executeCommand",
data: `Execute Command returned Error: ${line}`
},
returnResult: true
};
}
};
});Events
The modem instance emits the following public events:
| Event | Description |
| --- | --- |
| open | Port opened successfully |
| close | Port closed or modem went offline |
| error | Serial/modem error |
| job | New queued modem job |
| idle | Queue became empty |
| onSendingMessage | SMS is currently being sent |
| onMessageSent | SMS send success event |
| onMessageSendingFailed | SMS send failure event |
| onNewMessageIndicator | Incoming SMS indication |
| onNewMessage | Parsed incoming SMS payload |
| onNewIncomingCall | Incoming call indication |
| onMemoryFull | SIM memory full |
| onNewIncomingUSSD | Incoming USSD payload |
Example:
modem.on("onNewIncomingCall", async (data) => {
console.log("Incoming call", data);
});
modem.on("onNewMessage", (messages) => {
console.log("Incoming SMS", messages);
});Custom Raw Listener
You can attach a custom listener for modem-specific unsolicited lines:
modem.addModemListener({
match: (line) => line.startsWith("^BOOT:"),
process: (line) => {
console.log("Matched custom line", line);
}
});This is the safe replacement for overriding EventEmitter.addListener.
Callback vs Promise Note
Top-level list() uses Node-style callback ordering:
(error, result) => {}For historical compatibility, many modem instance callbacks use:
(result, error) => {}If you want the cleanest API, prefer the promise form:
const result = await modem.getNetworkSignal();Recommended Hardware Test Flow
For first-time modem validation, use this order:
list()open()initializeModem()setModemMode("PDU")getNetworkSignal()getModemSerial()getOwnNumber()(optional, may not be supported)checkSimMemory()getSimInbox()sendSMS()only after the above succeeds
Development
Build
npm run buildRun example
npm run exampleTroubleshooting
Port opens but modem does not answer AT commands
Check:
- correct COM/device path
- correct baud rate
- correct flow control settings
- modem is connected to the AT command port, not a data-only port
- runtime is Node.js, not Bun
getOwnNumber() fails
This is normal on many modems/SIMs. AT+CNUM support is inconsistent.
Incoming call does not terminate immediately
Carrier and modem behavior varies. This library currently prefers AT+CHUP and falls back to ATH, which is generally more reliable than ATH alone.
Repeated incoming call events
Some modems emit multiple +CLIP / call indications during the same ring cycle. If needed, debounce the event handler in your application.
License
MIT
