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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rtf-dm/artnet-lib

v1.2.0

Published

ArtNet library

Downloads

44

Readme

Logo

Installation

npm i @rtf-dm/artnet-lib

ArtNet-lib

ArtNet-lib is a flexible Node.js library written on TS that implements part of ArtNet protocol version 14. You can easily control DMX512 devices, detect and configure ArtNet nodes in your network.

Contact information

  • For any questions or suggestions about adding new device drivers in to a library, please contact with me by [email protected]
  • Right now, library development under private GitHub repository. It may be changed in the future.
  • Library is under active development process (Since it is part of a more complex system), so sometimes it may work unstably (sorry). However, I don't have a plans to change core functionality

Quick start guide

Initialize artnet library:


/*
You don't need to pass connection information by default.
However due to different network configurations, some times sending limited broadcast restricted.
So to calculate correct broadcast address you have to pass correct ip and netmask of your network
*/
const artNet = new ArtNetImpl({
    discovery: {
        sendReply: true // or false
    },
    network: {
        networkIp: '192.168.0.1',
        networkMask: '255.255.255.0',
        port:  ARTNET_PORT
    }
});

await artNet.init();

Wait for some ArtNet node reply on a discovery packet.

When you receive NEW_NODE_REGISTERED nodeManager automatically creates a new ArtNet node instance.


const [node1] = await artNet.nodeManager.waitFor('NEW_NODE_REGISTERED');
//or you can add event listener to 'NEW_NODE_REGISTERED' message
artNet.nodeManager.addListener('NEW_NODE_REGISTERED', (payload) => {
    console.log(
            payload.name, 
            payload.macAddress,
            payload.ipAddress,
            payload.lastResponseTime,
            payload.nodePortsInfo,
    )
});

Create a Universe instance and add a devices.

As for now, there are only 2 drivers present MixPanel150 for a specific device and Generic driver that allows you to control any DMX512 compatible device. The easiest way is to use a Generic driver and pass the number of channels captured by device. However, adding a new specialized driver is the best way to control devices.


//Create Universe with name 'my-Universe' with precreated list of Devices
const verse = artnet.createUniverse('my-Universe', [
 {deviceName: 'Generic', numChannelsCaptured: 5},
 {deviceName: 'MixPanel150'}, // there is no need set captured channels count since we know that mixpanel150 capture 12 channels
 {deviceName: "Generic", numChannelsCaptured: 10},
 {deviceName: "MixPanel150"}
]);

// Or create device manually and add it to Universe

const device = artNet.createDevice('Generic', 10);
verse.add(device);
// or set it instead of existing device
verse.setDevice(3, device);

Set device action to configure DMX512 data for your device attached to detected ArtNet Node.

You can perform the same command for all devices with the same driver or only for one device in a DMX chain addressed by your universe.

    verse.getDevice('Generic').forEach(device => {
    device.setAction({
        actionName: 'setChannels',
        parameters: {
            channels: [
                10, //channel 0
                25, //channel 1
                32, //...
                43,
                255,
            ]
        }
    })
});
    verse.getDevice<'Generic'>(0)?.setChannel(10, 255);
    verse.getDevice<'MixPanel150'>(1)?.setBrightness({
     percent: 100
    })

Update port info of detached universe

By default, created universe is detached. It means that you will be responsible for providing the correct port address to this universe. If the target node has a port address net: 0, subnet: 0, universe: 12, you should update universe address.

    verse.update(
        0, // - net,
        0, // - subnet,
        12. // - Universe
    )

Attach universe to node

You can attach a created universe to node port and node automatically update universe address

 // attach a created Universe to a specific node port
    artnet.nodeManager.attachUniverse(node1.name /*name of existed node*/, 0 /*port*/, verse);

Send a universe

After configuration, you can send an attached universe.

 await artnet.sendBroadcast(verse);  //Broadcast Universe to all nodes with the same port address 
 await artnet.nodeManager.syncAllNodes(); // Multicast universes to all Node. All nodes subscribed to Universe will updated the state of remote DMX chain 
 await artnet.nodeManager.getByMac(node1.macAddress).syncRemotePort(verse, 0); // Unicast Universe to specific node and port

Configure Artnet node

    await artNet.nodeManager.getByMac(nodeInfo.macAddress)?.configure({
      netSwitch: 2, // Net
      netSubSwitch: 12, //Subnet
      swOut: [1,2,3,4], // Out universes for ports
      longName: "The Best ArtNet node ever", // Long name is used as node-name in a library since some ArtNet Wi-Fi dongles have a lot of buggs with shortName
      swIn: [1,2,3,4] // In universes for ports (could be configured but currently unused)
    })
  • Full example
(async () => {
  const artnet = new ArtNetImpl({
    discovery: {
      sendReply: true,
    },
  });

  await artnet.init();


  const [node1] = await artnet.nodeManager.waitFor('NEW_NODE_REGISTERED');

  // Each time when node status or settings changed, 'NODE_STATUS_UPDATED' event fired
  artnet.nodeManager.addListener('NODE_STATUS_UPDATED', payload => console.log(`node updated: ${payload.name}`));

  // When node didn't response with poll reply for some time, it marked as dead and 'NODE_IS_DEAD' event fired;
  artnet.nodeManager.addListener('NODE_IS_DEAD', payload => console.log(`node dead: ${payload.name}`));

  await new Promise(resolve => setTimeout(() => resolve(1), 5000)); //Timeout to get node updated event

  artnet.discovery.sendArtPollReply = false; //disable reply on poll to demonstrate node dead event;

  await new Promise(resolve => setTimeout(() => resolve(1), 10000)); //Timeout to get node dead event

  artnet.discovery.sendArtPollReply = true;

  //Add Devices to created Universe mixpanel150 ([index 0]) - Generic ([index 1]) - MixPanel150 ([index 2]) - Generic([index 3]) ... e.t.c.
  const verse = artnet.createUniverse('my-Universe', [
    {deviceName: 'Generic', numChannelsCaptured: 5},
    {deviceName: 'MixPanel150'},
    {deviceName: 'Generic', numChannelsCaptured: 12},
    {deviceName: 'Generic', numChannelsCaptured: 2},
  ]);

  if (!verse) return null //Universe with the name 'my-Universe' already exists;

  //Set action for device group which implemented 'MixPanel150' interface and call api
  verse.getDevice('MixPanel150').forEach(device => {
    device.setBrightness({percent: 100});
    device.setLightMode({mode: 'BOOST'})
  })

  //Get a device by index 0 and assume it as a 'Generic' device
  verse.getDevice<'Generic'>(0)?.setChannels({
    channels: [1, 2, 3, 4, 0],
  });

  //Get a device by index 1 and assume it as 'MixPanel150' device
  verse.getDevice<'MixPanel150'>(1)?.setGreenMagentaBias({
    bias: -5,
  });

  // Set a single channel for a generic device with index 2 (Device index is a physical device order in DMX chain)
  verse.getDevice<'Generic'>(2)?.setChannel(0, 255);

  // Set channels [0, 1] to values [100, 255] respectively
  verse.getDevice<'Generic'>(3)?.setChannels({
    channels: [100, 255],
  });

  // Attach a created universe to node port 0.
  // This node will automatically update universe port when you call Node::syncRemotePort method of node instance
  // or when you call NodeManager::syncAllNodes()
  // Interface of this method will be changed in the nearest future (node.name => node.macAddress)
  artnet.nodeManager.attachUniverse(node1.name, 0, verse);


  // Broadcast a universe devices state to entire network.
  // Use this api only for detached universes. Since for controlled universes this doesn't make sense
  const sentBytes = await artnet.sendBroadcast(verse);

  console.log(`${sentBytes} bytes was sent`);

  //Send all attached universes of all nodes and all ports
  const sentBytesArray = await artnet.nodeManager.syncAllNodes();
  console.log(`${sentBytesArray} bytes was sent`);

  await artnet.dispose();
})();

It's possible to use High level API of ArtNetImpl instance to perform operations on DMX devices

(async() => {
  const artNet = new ArtNetImpl({
    discovery: {
      sendReply: true, //Only for debug purposes... automaticaly send reply to any poll messages
    }
  });

  //Init library
  await artNet.init(); 

  //Waiting for the artnet node
  artNet.nodeManager.waitFor('NEW_NODE_REGISTERED').then(async ([nodeInfo]) => {

    //Create Universe 'my_universe', and add Devices
    artNet.createUniverse('my_universe', [
      {
        deviceName: 'Generic',
        numChannelsCaptured: 10
      },
      {
        deviceName: 'Generic',
        numChannelsCaptured: 10
      }
    ]);


    // Broadcast Universe 'my_universe' and set actions for group of Devices that implements Generic interface 
    await artNet.broadcastUniverse({
      type: 'Group',
      universeName: 'my_universe',
      deviceGroup: 'Generic',
      action: {
        actionName: 'setChannel',
        parameters: {
          channel: 5,
          value: 10
        }
      }
    });

    // Broadcast Universe 'my_universe' and set actions for device wit index 0 in Universe.
    await artNet.broadcastUniverse({
      type: 'Exact',
      universeName: 'my_universe',
      deviceIndex: 0,
      action: {
        actionName: 'setChannel',
        parameters: {
          channel: 5,
          value: 10
        }
      }
    });

    // Attach Universe to detected node ports [1,2,3,4] ....
    // The Universe can be attached to numerous nodes
    artNet.attachUniverse(nodeInfo.macAddress, 0,'my_universe');
    artNet.attachUniverse(nodeInfo.macAddress, 1,'my_universe');
    artNet.attachUniverse(nodeInfo.macAddress, 2,'my_universe');
    artNet.attachUniverse(nodeInfo.macAddress, 3,'my_universe');

    //Muliticast attached to nodes universes
    await artNet.multicastUniverse({
      type: 'Group',
      universeName: 'my_universe',
      deviceGroup: 'Generic',
      action: {
        actionName: 'setChannel',
        parameters: {
          channel: 5,
          value: 10
        }
      }
    });

    //Unicast Universe to node with specific macAddress
    await artNet.unicastUniverse(nodeInfo.macAddress, {
      type: 'Group',
      universeName: 'my_universe',
      deviceGroup: 'Generic',
      action: {
        actionName: 'setChannel',
        parameters: {
          channel: 5,
          value: 10
        }
      }
    });

    //cleanup
    await artNet.dispose();
  });
})();

Change log

VER 1.2.0

  • ADD: ArtNetImpl::enumerateInterfacesInfo(): { address: string, netmask: string, mac: string } - returns available interfaces for connection
  • FIX: ArtNetImpl::changeNetwork() method works correctly now. Listening to udp packets only on target ip and port.
  • FIX: Discovery correct updating of polling intervals (Restart no needed).

VER 1.1.28

  • ADD: Detailed comments in readme
  • ADD: Example.ts
  • ADD: Logs for some methods

VER 1.1.25

  • FIX - remove .map files

VER 1.1.24

  • ADD: To setup lib debug output set DEV=1 environment variable
  • CLEANUP: Remove unnecessary logs...

VER 1.1.22

  • FIX: NodeManager will send only one "NODE_IS_DEAD" message
  • FIX: Added some missing imports to root index.js
  • Disable Logs for production