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

solarman-devices

v1.0.4

Published

Library for communicating with Solarman V5 compatible devices (inverters, loggers) via Modbus RTU over TCP

Readme

solarman-devices

Lightweight Node.js library for talking to Solarman V5 compatible devices - typically a WiFi/LAN logger stick (e.g. an LSW3) in front of a Deye/V-TAC hybrid inverter - over TCP, reading and writing its Modbus RTU registers.

This project is used by the Node-RED node node-red-contrib-solarman-devices and can be incorporated into other Node.js projects.

Status

Active development. The protocol layer (SolarmanV5, ModbusRtu) and the TCP client (Device) are implemented and verified against real hardware, not just synthetic test data - see test/ for regression tests built from captured device traffic.

Features

  • SolarmanV5 protocol framing - wraps/unwraps Modbus RTU frames into the Solarman V5 TCP envelope (start/length/control/sequence/serial header, checksum trailer).
  • Modbus RTU - read holding registers (0x03), read input registers (0x04), write single register (0x06), write multiple registers (0x10), with proper CRC16 and Modbus exception decoding.
  • Device client - manages the TCP connection lifecycle (connect, auto-reconnect, timeouts, one request in flight at a time), exposed as a small promise-based API.
  • MAC-based IP resolution - if a MAC address is configured, the current IP is looked up via ARP on every connect/reconnect, so the device keeps working across DHCP reassignments. Disable via disableArp on systems without ARP support.
  • Network discovery - Scanner/scan() broadcast the Hi-Flying-style UDP discovery probe and collect replies (IP, MAC, logger serial number) from Solarman V5 devices on the local network - useful for first-time setup when none of those are known yet.

Installation

npm install solarman-devices

Quick start

const { Device, scan } = require('solarman-devices')

// Discover devices on the local network (optional - skip if you already know
// the host/mac/serialNumber):
const found = await scan({ timeout: 2000 })
console.log(found) // [{ ip, mac, serialNumber }, ...]

const device = new Device({
  host: '192.168.1.100',       // fallback/initial IP - only used if mac is unset or ARP fails
  port: 8899,                  // Solarman V5 default port
  serialNumber: '1234567890',  // logger serial number (device sticker / web UI)
  unitAddress: 1,               // Modbus unit/slave address of the inverter
  mac: 'AA:BB:CC:DD:EE:FF',    // optional - keeps working across DHCP IP changes
  disableArp: false,           // set true on systems without arp/ip neighbor support
  timeout: 5000
})

device.on('status', (status) => console.log('status:', status))
device.connect()

// once status is 'connected':
const registers = await device.readHoldingRegisters(0, 2)
console.log(registers)

await device.writeSingleRegister(104, 500)

device.disconnect()

API overview

  • SolarmanV5 - the protocol envelope. wrapModbusFrame(modbusFrame) builds a request frame; unwrapModbusFrame(solarmanFrame) extracts the embedded Modbus RTU frame from a response.
  • ModbusRtu - frame builder/parser for one unit address. requestReadHoldingRegisters/requestReadInputRegisters/requestWriteSingleRegister/requestWriteMultipleRegisters build requests; the matching fetchXxx methods parse responses and throw ModbusError on a real Modbus exception.
  • Device - the TCP client tying both together: connect(), disconnect(), readHoldingRegisters(first, quantity), readInputRegisters(first, quantity), writeSingleRegister(register, value), writeMultipleRegisters(first, values). Emits status (connecting/connected/disconnected/error).
  • Scanner/scan() - Scanner is an EventEmitter (start()/stop(), emits find per device); scan(options) is a convenience one-shot wrapper that resolves with an array of { ip, mac, serialNumber } after options.timeout (default 2000ms).

Known limitations

  • The Modbus RTU response sequence number is compared for diagnostics but not enforced - real V-TAC/LSW3 traffic has been observed with a checksum- and control-code-valid response that still didn't echo back the exact request sequence number, on nearly every single response. A mismatch is accepted rather than rejected, and logged only via debugLog (the device node's Debug checkbox) rather than always-on warn - on this kind of hardware it isn't an exceptional event worth flagging every time, just the normal case.
  • The exact byte layout of a response frame's body prefix (as opposed to the request side, which mirrors the real pysolarmanv5 construction field-for-field) is only known empirically, not derived from a documented spec - see the comment at the top of lib/solarman_v5.js.

Development & Tests

npm test

Contributing

PRs welcome. Please follow the existing code style: catch and else should start on their own line, and use multi-line if/else blocks.

Changelog

Disclaimer

The software is provided as-is under the MIT license. The author cannot be held responsible for any unintended behaviours.

Thanks

If you like our ideas and want to support further development, you can donate here:
Donate Donate
Please understand that requests from those who support us will be given priority