npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

serialport-gsm-ts

v1.0.0

Published

Serialport implementation for GSM commands (send text/flash messages, read, etc.)

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-ts

Package 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 example

That 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:

  1. list()
  2. open()
  3. initializeModem()
  4. setModemMode("PDU")
  5. getNetworkSignal()
  6. getModemSerial()
  7. getOwnNumber() (optional, may not be supported)
  8. checkSimMemory()
  9. getSimInbox()
  10. sendSMS() only after the above succeeds

Development

Build

npm run build

Run example

npm run example

Troubleshooting

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