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

@2bad/tvt

v2.1.0

Published

TypeScript SDK for TVT and rebadged OEM CCTV devices: login, live H.264 streaming, snapshots, alarm monitoring, RTSP, and device control over the NET_SDK protocol

Readme

TVT Device SDK

NPM version License GitHub Build Status Code coverage Written in TypeScript

TypeScript bindings for TVT (Tongwei Video Technology) CCTV devices: IP cameras, NVRs, DVRs. Talks the proprietary NET_SDK protocol through the vendor's libdvrnetsdk.so via koffi FFI. There's no usable English documentation, so this was built by disassembling the vendor SDK and reverse engineering the wire protocol from packet captures. The Wireshark dissectors from that work are in proto/.

Everything is async. Native calls don't block the event loop, and callbacks from the SDK's own threads get marshalled back into JS properly.

Requirements

  • Linux x86_64
  • Node.js >= 26

The required .so files ship with the package under bin/linux and are loaded automatically. No system-wide installation, no LD_LIBRARY_PATH stuff.

Compatibility

TVT is mostly a white-label manufacturer, so their hardware gets sold under a lot of other names. If a device answers on port 9008 or works with TVT's SuperLive/SuperCam apps, it's almost certainly a rebadged TVT and this SDK should talk to it.

Brands known to sell rebadged TVT devices:

  • Digital Watchdog (some product lines)
  • Q-See
  • CP Plus
  • Provision-ISR
  • Avycon
  • TeleEye
  • NoVus
  • Meriva Security
  • InVid Tech
  • Alibi (Observint)
  • TecVoz
  • JFL Alarmes
  • Gazer
  • Questek
  • Technomate

The long tail is much bigger: pulling the OEM logo files out of TVT firmware turned up 79 brands (IPVM has the list). Most of these brands source from several manufacturers though, so not every product they sell is TVT. And GE's TruVision "TVT-xxxx" model numbers have nothing to do with TVT, that's just a product-line prefix.

Install

npm install @2bad/tvt

Quick start

import { Device } from '@2bad/tvt'

// 9008 port for cameras, 6036 for NVRs
const device = await Device.create('192.168.1.100', 9008)
await device.login('admin', 'password')

const info = await device.getInfo()
console.log(`${info.deviceName} (${info.deviceProduct}), ${info.videoInputNum} video inputs`)

await device.saveSnapshot(0, '/tmp/ch0.jpg')

await device.dispose()

dispose() stops any running streams and alarm monitoring, logs out, and releases the SDK. Call it exactly once, at the end.

Examples

Runnable examples live in docs/examples:

API

class Device {
  static create(ip: string, port?: number, settings?: Settings): Promise<Device>

  login(user: string, pass: string): Promise<boolean>
  logout(): Promise<boolean>
  getInfo(): Promise<DeviceInfo>
  get version(): VersionInfo

  startLiveStream(channel?: number, streamType?: STREAM_TYPE): Promise<LiveStream>
  saveSnapshot(channel: number, filePath: string): Promise<boolean>
  captureSnapshot(channel?: number, bufferSize?: number): Promise<Buffer>
  getRtspUrl(channel?: number, streamType?: STREAM_TYPE, options?: RtspUrlOptions): Promise<string>
  getStreamCount(channel?: number): Promise<number>

  searchRecordings(channel: number, start: Date, stop: Date): Promise<RecFile[]>
  downloadRecording(
    channel: number,
    start: Date,
    stop: Date,
    filePath: string,
    options?: { format?: RECORDING_FORMAT; streamType?: STREAM_TYPE; onProgress?: (percent: number) => void }
  ): Promise<boolean>
  startPlayback(channel: number, start: Date, stop: Date): Promise<PlaybackStream>

  startAlarmMonitoring(onAlarm: AlarmCallback): Promise<boolean>
  stopAlarmMonitoring(): Promise<boolean>
  triggerAlarm(value: boolean): Promise<boolean>

  getTime(): Promise<Date>
  setTime(time?: Date): Promise<boolean>
  getVideoEffect(channel?: number): Promise<VideoEffect>
  setVideoEffect(channel: number, effect: VideoEffect): Promise<boolean>

  reboot(): Promise<boolean>
  shutdown(): Promise<boolean>

  getLastError(): Promise<string>
  dispose(): Promise<boolean>
}

class LiveStream {
  frames(): AsyncGenerator<LiveFrame>
  recordTo(filePath: string): Promise<boolean>
  stopRecording(): Promise<boolean>
  stop(): Promise<boolean>
  get stopped(): boolean
}

class PlaybackStream {
  frames(): AsyncGenerator<PlaybackFrame>
  recordTo(filePath: string): Promise<boolean>
  stopRecording(): Promise<boolean>
  pause(): Promise<boolean>
  resume(): Promise<boolean>
  fastForward(): Promise<boolean>
  rewind(): Promise<boolean>
  frameStep(): Promise<boolean>
  normalSpeed(): Promise<boolean>
  stop(): Promise<boolean>
  get stopped(): boolean
}

Settings on Device.create controls the native connection behavior: connectionTimeoutMs (5000), maxRetries (3), reconnectIntervalMs (30000), isReconnectEnabled (true).

Every method except create, login, and getLastError requires a prior successful login and throws otherwise.

Error semantics

Methods that return data (getInfo, captureSnapshot, getTime, getRtspUrl, getVideoEffect, startLiveStream, searchRecordings, startPlayback, downloadRecording, login) throw on failure with the decoded NET_SDK error name. Methods that perform an action (saveSnapshot, triggerAlarm, setTime, logout, ...) resolve false on failure. getLastError() returns the name of the most recent SDK error; the full table is exported as NET_SDK_ERROR.

Debug logging

Namespaced under debug:

DEBUG=tvt:* node app.js       # everything
DEBUG=tvt:perf node app.js    # native call timings only

Repository layout

bin/       vendor .so libraries (libdvrnetsdk.so and dependencies)
docs/
  examples/       usage examples
  vendor/         vendor SDK reference: NET_SDK manuals (PDF/CHM), C headers, demo sources
proto/     Wireshark dissectors (Lua) for the wire protocol
source/    the TypeScript implementation
  device.ts       Device class, public entry point
  stream.ts       LiveStream
  playback.ts     PlaybackStream
  lib/sdk.ts      typed FFI layer over libdvrnetsdk.so
  lib/struct/     koffi struct definitions mirroring the C headers

proto/ipc.lua dissects the device protocol in Wireshark. Useful if you're extending the bindings or poking at firmware behavior.

Development

git clone https://github.com/2BAD/tvt.git
cd tvt
pnpm install
pnpm build          # tsdown
pnpm check          # oxlint + oxfmt + tsc
pnpm test:unit      # vitest, no hardware needed
pnpm test:integration   # requires a real device on the network

Migration from v1.x

Construction is now a factory and async: new Device(ip) becomes await Device.create(ip). Every SDK call returns a Promise: device.info becomes await device.getInfo().

License

MIT. See LICENSE.

Disclaimer

Not affiliated with or endorsed by TVT Digital Technology Co., Ltd. This is an independent implementation built on reverse engineering. The vendor libraries in bin/ are redistributed as-is. Point it only at devices you own or are authorized to manage.