Skip to content

Repository files navigation

nut-client

NPM version CI Coverage Downloads License Known Vulnerabilities nut-client-snyk Donate GitHub stars Package Quality

Bugs Code Smells Duplicated Lines (%) Lines of Code Maintainability Rating Quality Gate Status Reliability Rating Security Rating Technical Debt Vulnerabilities

Dependencies update - renovate NPM

Description

nut-client is a Node.js client for Network UPS Tools (NUT), enabling advanced communication with NUT servers for UPS management. It is built to be robust, performant, and easy to integrate, supporting parallel requests, automatic reconnection, and event-based monitoring via Promises.

Installation

npm install nut-client
# or
pnpm add nut-client
# or
yarn add nut-client

Supported Node.js versions: >=18

Features

  • NUT Command Support : Most NUT commands are supported, with automatic parsing of responses to simplify integration. Additionally, a manual mode is available for full control.

    import { NUTClient } from 'nut-client'
    
    const client = new NUTClient('127.0.0.1', 3493);
    
    console.log(await client.listUPS());
    console.log(await client.listVariables('ups'));
    
    // Run an instant command with optional parameter
    await client.runCommand('myups', 'shutdown.return', '60');
    
    // Manual command
    console.log(await client.send(['LIST', 'VAR', 'myups']));
  • Parallel Request Handling : Unlike other NUT libraries, nut-client manages an internal queue to handle parallel requests without conflicts, using Promises for efficient request handling.

    import { NUTClient } from 'nut-client'
    
    const client = new NUTClient('127.0.0.1', 3493);
    
    const [ups1, ups2, ups3] = await Promise.all([
      client.getUPS('ups1'),
      client.getUPS('ups2'),
      client.getUPS('ups3'),
    ])
  • Auto-Reconnect : The client can automatically reconnect on connection loss with exponential backoff, jitter, and configurable limits. Credentials and TLS state are restored automatically.

    import { NUTClient } from 'nut-client'
    
    const client = new NUTClient('127.0.0.1', 3493, {
      autoReconnect: true,
      username: 'admin',
      password: 'secret',
      reconnectDelay: 1000,        // initial delay (ms)
      maxReconnectDelay: 30000,    // cap for exponential backoff
      reconnectBackoff: 2,         // multiplier
      maxReconnectAttempts: 10,    // give up after N attempts
    });
    
    client.on('reconnected', () => console.log('Reconnected!'));
    client.on('reconnectExhausted', () => console.log('No more retries'));
    
    // Or use the static factory for connect + auth in one call
    const client2 = await NUTClient.create('127.0.0.1', 3493, {
      username: 'admin',
      password: 'secret',
      autoReconnect: true,
    });
  • StartTLS Support : Communicate securely with the NUT server using StartTLS for encryption. TLS state is automatically restored on reconnect.

    import { NUTClient } from 'nut-client'
    
    const client = new NUTClient('127.0.0.1', 3493);
    
    // Use clear TCP connection
    console.log(await client.version());
    
    await client.startTLS({
      // Allow self-signed certificate
      rejectUnauthorized: false
    });
    
    // Use encrypted TCP connection
    console.log(await client.version());
  • UPS Object with Convenience Methods : Get a typed UPS object with high-level helpers for status, battery, load, and more.

    const client = new NUTClient('127.0.0.1', 3493);
    const ups = await client.getUPS('myups');
    
    if (ups) {
      console.log('Description:', ups.description);
    
      const status = await ups.getStatus();    // ENUTStatus[] (e.g. ['OL', 'CHRG'])
      console.log('Online:', await ups.isOnline());
      console.log('On battery:', await ups.isOnBattery());
      console.log('Battery charge:', await ups.getBatteryCharge());  // 0-100 or NaN
      console.log('Runtime:', await ups.getBatteryRuntime());        // seconds or NaN
      console.log('Load:', await ups.getLoad());                     // 0-100 or NaN
      console.log('Model:', await ups.getModel());
      console.log('Manufacturer:', await ups.getManufacturer());
      console.log('Serial:', await ups.getSerial());
      console.log('Input voltage:', await ups.getInputVoltage());
      console.log('Output voltage:', await ups.getOutputVoltage());
    }
  • Built-in Monitor : A Monitor module reads variables at regular intervals, emitting UPS events similar to upsmon (plus additional "NOT" events and variable change tracking). It integrates with the auto-reconnect system — when the client reconnects, the Monitor resumes automatically; when reconnect is exhausted, it emits NOCOMM.

    import { NUTClient, Monitor } from 'nut-client'
    
    const client = new NUTClient('127.0.0.1', 3493);
    const monitor = new Monitor(client, 'myUps');
    
    // Status events (when a flag appears)
    monitor.on('ONLINE', () => console.log('UPS back online'));
    monitor.on('ONBATT', () => console.log('UPS on battery'));
    monitor.on('LOWBATT', () => console.log('Battery low'));
    
    // "NOT" events (when a flag disappears)
    monitor.on('NOTOL', () => console.log('No longer online'));
    monitor.on('NOTOB', () => console.log('No longer on battery'));
    monitor.on('NOTLB', () => console.log('Battery no longer low'));
    monitor.on('NOTFSD', () => console.log('FSD cleared'));
    monitor.on('NOTRB', () => console.log('Battery replacement cleared'));
    monitor.on('NOTCAL', () => console.log('Calibration finished'));
    monitor.on('NOTOFF', () => console.log('UPS no longer off'));
    monitor.on('NOTBYPASS', () => console.log('No longer on bypass'));
    
    // Communication events
    monitor.on('COMMOK', () => console.log('Communication restored'));
    monitor.on('COMMBAD', () => console.log('Communication lost'));
    monitor.on('NOCOMM', () => console.log('Reconnect exhausted'));
    
    // Variable change events
    monitor.on('VARIABLE_CHANGED', (key, oldValue, newValue, oldVars, newVars) => {
      console.log(`${key}: ${oldValue}${newValue}`);
    });
    
    // Fired when any variable changed in a poll cycle
    monitor.on('VARIABLES_CHANGED', (oldVars, newVars) => {
      console.log('Variables updated');
    });
    
    monitor.on('BATTERY_CHARGE', (charge, raw) => {
      console.log(`Battery: ${charge}%`);
    });
    
    // Wildcard listener for debugging
    monitor.on('*', (event, ...args) => {
      console.log(`Event: ${event}`, args);
    });
    
    await monitor.start();
    
    // Pause/resume without full restart
    monitor.pause();
    monitor.isPaused();  // check if paused
    monitor.resume();
    
    // Check lifecycle state
    monitor.isDestroyed();
    
    // Cleanup
    monitor.destroy();

    Full event list available in the TypeDoc documentation.

  • Command Tracking : For long-running write operations, enable tracking to get a UUID per command and poll for completion. Only write commands (SET VAR, INSTCMD) are tracked; reads are unaffected. Requires NUT 2.8.0+ (protocol v1.3).

    Manual polling:

    import { NUTClient } from 'nut-client';
    
    const client = new NUTClient('127.0.0.1', 3493);
    await client.connect('user', 'secret');
    
    // Enable tracking
    await client.setTracking(true);
    
    // Long-running command returns immediately with a tracking UUID
    const result = await client.runCommand('myups', 'shutdown.return', '60');
    // result = { tracked: true, trackingUid: 'abc-123-def' }
    
    if (result.tracked && 'trackingUid' in result) {
      // Poll until the command completes
      let status;
      do {
        await new Promise((resolve) => setTimeout(resolve, 5000));
        status = await client.getTracking(result.trackingUid);
        console.log('Status:', status);
      } while (status === 'PENDING');
    
      if (status === 'SUCCESS') {
        console.log('Shutdown completed');
      } else {
        console.log('Shutdown failed');
      }
    }
    
    // Disable tracking when done
    await client.setTracking(false);

    Automatic polling with followTracking:

    // Enable tracking
    await client.setTracking(true);
    
    // Command with automatic polling — resolves when complete
    const result = await client.runCommand('myups', 'shutdown.return', '60', {
      followTracking: true,
      trackingTimeout: 60000,      // max wait time (default: 30s)
      trackingPollInterval: 5000   // poll interval (default: 1s)
    });
    // result = { tracked: true, status: 'SUCCESS' } | { tracked: true, status: 'ERR' }
    
    if (result.tracked && result.status === 'SUCCESS') {
      console.log('Shutdown completed');
    }

    See the NUT network protocol documentation for details on the tracking protocol.

  • UPS Management Commands : Additional server-side operations like getting descriptions and forcing shutdowns.

    const client = new NUTClient('127.0.0.1', 3493);
    
    // Get the UPS description (from ups.conf desc= field)
    const desc = await client.getUPSDescription('myups');
    
    // Force a shutdown (sets the FSD flag — requires master/FSD permission)
    await client.forceShutdown('myups');
  • Fully Typed with TypeScript (ESM + CJS) : Built with TypeScript, nut-client is distributed in both ESM and CommonJS modules for maximum compatibility.

API Overview

Class Description
NUTClient High-level client facade with auto-reconnect, tracking, and typed parsing
RawNUTClient Low-level TCP client for raw NUT protocol access (advanced use)
UPS Typed representation of a UPS device with convenience methods
Monitor Event-based UPS monitoring with status, variable change, and communication events

NUTClient Static Factory

// Create and authenticate in one call
const client = await NUTClient.create('127.0.0.1', 3493, {
  username: 'admin',
  password: 'secret',
  autoReconnect: true,
});

NUTClient Events

client.on('disconnected', () => {});
client.on('reconnecting', (attempt, delay) => {});
client.on('reconnected', () => {});
client.on('reconnectFailed', (attempt) => {});
client.on('reconnectExhausted', () => {});
client.on('destroyed', () => {});

Error Handling

nut-client throws typed errors that map to NUT protocol error codes. All errors extend NUTProtocolError:

import { AccessDeniedError, UnknownUPSError, ConnectionLostError } from 'nut-client';

try {
  await client.getVariable('myups', 'battery.charge');
} catch (e) {
  if (e instanceof AccessDeniedError) {
    console.log('Authentication required');
  } else if (e instanceof UnknownUPSError) {
    console.log('UPS not found');
  } else if (e instanceof ConnectionLostError) {
    console.log('Connection lost');
  }
}

Cleanup

// Destroy the client (releases TCP socket, clears timers, removes listeners)
client.destroy();

// Destroy a monitor
monitor.destroy();

Debug

This library includes debug. To enable debug logging:

DEBUG=nut-client:* node my-script.js

Contributing

Contributions are welcome! If you have suggestions, feel free to open an issue or a pull request.

git clone https://github.com/thib3113/nut.git
cd nut
pnpm install
pnpm run build
pnpm run test

License

MIT

About

A NUT (Network UPS Tools) Client

Topics

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages